gonna figure it out
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
Copyright (c) 2013 Eric Lemoine
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -0,0 +1,52 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: GeoAlchemy2
|
||||
Version: 0.15.2
|
||||
Summary: Using SQLAlchemy with Spatial Databases
|
||||
Home-page: https://geoalchemy-2.readthedocs.io/en/stable/
|
||||
Author: Eric Lemoine
|
||||
Author-email: eric.lemoine@gmail.com
|
||||
License: MIT
|
||||
Project-URL: Tracker, https://github.com/geoalchemy/geoalchemy2/issues
|
||||
Project-URL: Source, https://github.com/geoalchemy/geoalchemy2
|
||||
Keywords: geo,gis,sqlalchemy,orm
|
||||
Classifier: Development Status :: 4 - Beta
|
||||
Classifier: Environment :: Plugins
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Intended Audience :: Information Technology
|
||||
Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Topic :: Scientific/Engineering :: GIS
|
||||
Requires-Python: >=3.7
|
||||
License-File: COPYING.rst
|
||||
Requires-Dist: SQLAlchemy >=1.4
|
||||
Requires-Dist: packaging
|
||||
Provides-Extra: shapely
|
||||
Requires-Dist: Shapely >=1.7 ; extra == 'shapely'
|
||||
|
||||
============
|
||||
GeoAlchemy 2
|
||||
============
|
||||
|
||||
.. image:: https://github.com/geoalchemy/geoalchemy2/actions/workflows/test_and_publish.yml/badge.svg?branch=master
|
||||
:target: https://github.com/geoalchemy/geoalchemy2/actions
|
||||
|
||||
.. image:: https://coveralls.io/repos/geoalchemy/geoalchemy2/badge.png?branch=master
|
||||
:target: https://coveralls.io/r/geoalchemy/geoalchemy2
|
||||
|
||||
.. image:: https://readthedocs.org/projects/geoalchemy-2/badge/?version=latest
|
||||
:target: https://geoalchemy-2.readthedocs.io/en/latest/?badge=latest
|
||||
:alt: Documentation Status
|
||||
|
||||
.. image:: https://zenodo.org/badge/5638538.svg
|
||||
:target: https://zenodo.org/doi/10.5281/zenodo.10808783
|
||||
|
||||
GeoAlchemy 2 is a Python toolkit for working with spatial databases. It is
|
||||
based on the gorgeous `SQLAlchemy <http://www.sqlalchemy.org/>`_.
|
||||
|
||||
Documentation is on Read the Docs: https://geoalchemy-2.readthedocs.io/en/stable.
|
||||
@@ -0,0 +1,57 @@
|
||||
GeoAlchemy2-0.15.2.dist-info/COPYING.rst,sha256=-bQKftq9uMOROzF7oN65kYBBIJKxTmBuDoftp27IC3I,1056
|
||||
GeoAlchemy2-0.15.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
GeoAlchemy2-0.15.2.dist-info/METADATA,sha256=7ti009u_zE-vAPIyGyfVcC-oZzloZ-TYOKOV7ZahmKo,2087
|
||||
GeoAlchemy2-0.15.2.dist-info/RECORD,,
|
||||
GeoAlchemy2-0.15.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
GeoAlchemy2-0.15.2.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
|
||||
GeoAlchemy2-0.15.2.dist-info/top_level.txt,sha256=3kGUTcfBeXd61zFpof6-qiuw1peNF_HuZabgHQgrdis,12
|
||||
geoalchemy2/__init__.py,sha256=Wb5f11AM_hYn56yrxOAOjCT3z76TCtseqjFo2hKsob4,1971
|
||||
geoalchemy2/__pycache__/__init__.cpython-312.pyc,,
|
||||
geoalchemy2/__pycache__/_functions.cpython-312.pyc,,
|
||||
geoalchemy2/__pycache__/_functions_helpers.cpython-312.pyc,,
|
||||
geoalchemy2/__pycache__/alembic_helpers.cpython-312.pyc,,
|
||||
geoalchemy2/__pycache__/comparator.cpython-312.pyc,,
|
||||
geoalchemy2/__pycache__/elements.cpython-312.pyc,,
|
||||
geoalchemy2/__pycache__/exc.cpython-312.pyc,,
|
||||
geoalchemy2/__pycache__/functions.cpython-312.pyc,,
|
||||
geoalchemy2/__pycache__/shape.cpython-312.pyc,,
|
||||
geoalchemy2/__pycache__/utils.cpython-312.pyc,,
|
||||
geoalchemy2/_functions.py,sha256=e8v584Fx_fmh8XnJvkuYEXrcyPDiWV95Mu2yw6L-LHY,62863
|
||||
geoalchemy2/_functions_helpers.py,sha256=hVM9DDTgf-oM27EZu6ycW1y2ENnSxUJixdINetWsu0E,2651
|
||||
geoalchemy2/admin/__init__.py,sha256=s8L5C9pYC5dTxhIp9j-JzWeg9kEJevXUW8Pq4_L4aZ8,3984
|
||||
geoalchemy2/admin/__pycache__/__init__.cpython-312.pyc,,
|
||||
geoalchemy2/admin/dialects/__init__.py,sha256=XodzuBbWKkkSQzM5EL3I33azuE-y_go0nVOlmIyJ13g,367
|
||||
geoalchemy2/admin/dialects/__pycache__/__init__.cpython-312.pyc,,
|
||||
geoalchemy2/admin/dialects/__pycache__/common.cpython-312.pyc,,
|
||||
geoalchemy2/admin/dialects/__pycache__/geopackage.cpython-312.pyc,,
|
||||
geoalchemy2/admin/dialects/__pycache__/mysql.cpython-312.pyc,,
|
||||
geoalchemy2/admin/dialects/__pycache__/postgresql.cpython-312.pyc,,
|
||||
geoalchemy2/admin/dialects/__pycache__/sqlite.cpython-312.pyc,,
|
||||
geoalchemy2/admin/dialects/common.py,sha256=8OkGa7T2Gxz2vDVnC9nva8Ma0YqBwGxHWfBJoZF1cNQ,2936
|
||||
geoalchemy2/admin/dialects/geopackage.py,sha256=UaJignKtlo13oHxq92yescVJtXFEXkm4fy5WzR6sLT8,13469
|
||||
geoalchemy2/admin/dialects/mysql.py,sha256=z2gmkTGav60_myDCFxO1yeezDKBcNcMxFvcpH-OqVKw,6867
|
||||
geoalchemy2/admin/dialects/postgresql.py,sha256=VwB_h3TC8M5aQ6aKE3UYgxHfbEKav3eIHJeLx534Zzg,6191
|
||||
geoalchemy2/admin/dialects/sqlite.py,sha256=UYQuNDDy-1-lsvYlqyKy01UtHgLF4iEN42fE5D-9QQk,13597
|
||||
geoalchemy2/alembic_helpers.py,sha256=WYjKiVVyHtlB4DI22z-G0-x8NnG8othAINUwmxBCMcs,27885
|
||||
geoalchemy2/comparator.py,sha256=WUDXn10doDlJVnYiWbVIAhhBu7N5AO9BI8sNf2mhpA4,8100
|
||||
geoalchemy2/elements.py,sha256=5yd_7dUQGbrLvgYvo6A2YYAFwTvP4_BskIdBCB0DrlM,13002
|
||||
geoalchemy2/exc.py,sha256=Nn9bRKB_35skWDMkEf4_Y2GL6gvguPVycPZcbOfa69g,226
|
||||
geoalchemy2/functions.py,sha256=0he8hy_SAWpXAFPdfg32NMW5G0FaZP7yVl0jn0MRLBQ,10320
|
||||
geoalchemy2/functions.pyi,sha256=rKCKdDSVTgeUQHItkOKqJSEuyKmVJ30bpjLpCPwMI5g,108636
|
||||
geoalchemy2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
geoalchemy2/shape.py,sha256=TZJIhCN4p4ZVwZMXidxYxTMeeyReAyoFzyYwspJRqSA,2792
|
||||
geoalchemy2/types/__init__.py,sha256=dD2-E8rr1nXr_d4CqyiEvRjO1H-dDRQnzr5B1g3_an8,14186
|
||||
geoalchemy2/types/__pycache__/__init__.cpython-312.pyc,,
|
||||
geoalchemy2/types/dialects/__init__.py,sha256=sw__RqGAFVrUPGrICjsva9SPoYLBNfyAkBHmsJkT7k0,359
|
||||
geoalchemy2/types/dialects/__pycache__/__init__.cpython-312.pyc,,
|
||||
geoalchemy2/types/dialects/__pycache__/common.cpython-312.pyc,,
|
||||
geoalchemy2/types/dialects/__pycache__/geopackage.cpython-312.pyc,,
|
||||
geoalchemy2/types/dialects/__pycache__/mysql.cpython-312.pyc,,
|
||||
geoalchemy2/types/dialects/__pycache__/postgresql.cpython-312.pyc,,
|
||||
geoalchemy2/types/dialects/__pycache__/sqlite.cpython-312.pyc,,
|
||||
geoalchemy2/types/dialects/common.py,sha256=gxKaRQhIODJhu_yBurlvFNAqGh3LfPNu_DcObEG1wlU,138
|
||||
geoalchemy2/types/dialects/geopackage.py,sha256=nRmN_PnF-CWMEHkWhKVdsybnw3SvXZIBXAHIHXJLTqw,147
|
||||
geoalchemy2/types/dialects/mysql.py,sha256=zMNi1920v0XlVaCJWSwtq0b0P5YQRn8y3X_rBxC5KEw,1790
|
||||
geoalchemy2/types/dialects/postgresql.py,sha256=7NBKEbDJXMwX8Sgs6o_N2bAUHgjUcjbrdmYOA7sDiRw,1117
|
||||
geoalchemy2/types/dialects/sqlite.py,sha256=B-yLzaQcqL_4dXoOPX9D9IXw2ZQlq-Tibv_kqUIBeb4,2104
|
||||
geoalchemy2/utils.py,sha256=OYWYnT64tjp4DWhPdq3KIxHbVti932xPMtGGxajtu-I,488
|
||||
@@ -0,0 +1,5 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: setuptools (70.3.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
geoalchemy2
|
||||
70
.venv/lib/python3.12/site-packages/geoalchemy2/__init__.py
Normal file
70
.venv/lib/python3.12/site-packages/geoalchemy2/__init__.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""GeoAlchemy2 package."""
|
||||
|
||||
from geoalchemy2 import admin
|
||||
from geoalchemy2 import elements # noqa
|
||||
from geoalchemy2 import exc # noqa
|
||||
from geoalchemy2 import functions # noqa
|
||||
from geoalchemy2 import shape # noqa
|
||||
from geoalchemy2 import types # noqa
|
||||
from geoalchemy2.admin.dialects.geopackage import load_spatialite_gpkg # noqa
|
||||
from geoalchemy2.admin.dialects.sqlite import load_spatialite # noqa
|
||||
from geoalchemy2.elements import CompositeElement # noqa
|
||||
from geoalchemy2.elements import RasterElement # noqa
|
||||
from geoalchemy2.elements import WKBElement # noqa
|
||||
from geoalchemy2.elements import WKTElement # noqa
|
||||
from geoalchemy2.exc import ArgumentError # noqa
|
||||
from geoalchemy2.types import Geography # noqa
|
||||
from geoalchemy2.types import Geometry # noqa
|
||||
from geoalchemy2.types import Raster # noqa
|
||||
|
||||
admin.setup_ddl_event_listeners()
|
||||
|
||||
|
||||
# Get version number
|
||||
__version__ = "UNKNOWN VERSION"
|
||||
|
||||
# Attempt to use importlib.metadata first because it's much faster
|
||||
# though it's only available in Python 3.8+ so we'll need to fall
|
||||
# back to pkg_resources for Python 3.7 support
|
||||
try:
|
||||
import importlib.metadata
|
||||
except ImportError:
|
||||
try:
|
||||
from pkg_resources import DistributionNotFound
|
||||
from pkg_resources import get_distribution
|
||||
except ImportError: # pragma: no cover
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
__version__ = get_distribution("GeoAlchemy2").version
|
||||
except DistributionNotFound: # pragma: no cover
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
__version__ = importlib.metadata.version("GeoAlchemy2")
|
||||
except importlib.metadata.PackageNotFoundError: # pragma: no cover
|
||||
pass
|
||||
|
||||
|
||||
__all__ = [
|
||||
"__version__",
|
||||
"ArgumentError",
|
||||
"CompositeElement",
|
||||
"Geography",
|
||||
"Geometry",
|
||||
"Raster",
|
||||
"RasterElement",
|
||||
"WKBElement",
|
||||
"WKTElement",
|
||||
"admin",
|
||||
"elements",
|
||||
"exc",
|
||||
"load_spatialite",
|
||||
"load_spatialite_gpkg",
|
||||
"shape",
|
||||
"types",
|
||||
]
|
||||
|
||||
|
||||
def __dir__():
|
||||
return __all__
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
829
.venv/lib/python3.12/site-packages/geoalchemy2/_functions.py
Normal file
829
.venv/lib/python3.12/site-packages/geoalchemy2/_functions.py
Normal file
@@ -0,0 +1,829 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# flake8: noqa
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
from geoalchemy2 import types
|
||||
|
||||
# fmt: off
|
||||
_FUNCTIONS: List[Tuple[str, Optional[type], Union[None, str, Tuple[str, str]]]] = [
|
||||
('AddGeometryColumn', None,
|
||||
'''Adds a geometry column to an existing table.'''),
|
||||
('DropGeometryColumn', None,
|
||||
'''Removes a geometry column from a spatial table.'''),
|
||||
('DropGeometryTable', None,
|
||||
'''Drops a table and all its references in geometry_columns.'''),
|
||||
('Find_SRID', None,
|
||||
'''Returns the SRID defined for a geometry column.'''),
|
||||
('Populate_Geometry_Columns', None,
|
||||
'''Ensures geometry columns are defined with type modifiers or have appropriate spatial constraints.'''),
|
||||
('UpdateGeometrySRID', None,
|
||||
'''Updates the SRID of all features in a geometry column, and the table metadata.'''),
|
||||
('ST_Collect', types.Geometry,
|
||||
'''Creates a GeometryCollection or Multi* geometry from a set of geometries.'''),
|
||||
('ST_LineFromMultiPoint', types.Geometry,
|
||||
'''Creates a LineString from a MultiPoint geometry.'''),
|
||||
('ST_MakeEnvelope', types.Geometry,
|
||||
'''Creates a rectangular Polygon from minimum and maximum coordinates.'''),
|
||||
('ST_MakeLine', types.Geometry,
|
||||
'''Creates a Linestring from Point, MultiPoint, or LineString geometries.'''),
|
||||
('ST_MakePoint', types.Geometry,
|
||||
'''Creates a 2D, 3DZ or 4D Point.'''),
|
||||
('ST_MakePointM', types.Geometry,
|
||||
'''Creates a Point from X, Y and M values.'''),
|
||||
('ST_MakePolygon', types.Geometry,
|
||||
'''Creates a Polygon from a shell and optional list of holes.'''),
|
||||
('ST_Point', types.Geometry,
|
||||
'''Creates a Point with the given coordinate values. Alias for ST_MakePoint.'''),
|
||||
('ST_Polygon', types.Geometry,
|
||||
'''[geometry] Creates a Polygon from a LineString with a specified SRID.\nOR\n[raster] Returns a multipolygon geometry formed by the union of pixels that have a pixel value that is not no data value. If no band number is specified, band num defaults to 1.'''),
|
||||
('ST_TileEnvelope', types.Geometry,
|
||||
'''Creates a rectangular Polygon in Web Mercator (SRID:3857) using the XYZ tile system.'''),
|
||||
('GeometryType', None,
|
||||
'''Returns the type of a geometry as text.'''),
|
||||
('ST_Boundary', types.Geometry,
|
||||
'''Returns the boundary of a geometry.'''),
|
||||
('ST_CoordDim', None,
|
||||
'''Return the coordinate dimension of a geometry.'''),
|
||||
('ST_Dimension', None,
|
||||
'''Returns the topological dimension of a geometry.'''),
|
||||
('ST_Dump', types.GeometryDump,
|
||||
'''Returns a set of geometry_dump rows for the components of a geometry.'''),
|
||||
('ST_DumpPoints', types.GeometryDump,
|
||||
'''Returns a set of geometry_dump rows for the points in a geometry.'''),
|
||||
('ST_DumpRings', types.GeometryDump,
|
||||
'''Returns a set of geometry_dump rows for the exterior and interior rings of a Polygon.'''),
|
||||
('ST_EndPoint', types.Geometry,
|
||||
'''Returns the last point of a LineString or CircularLineString.'''),
|
||||
('ST_Envelope', types.Geometry,
|
||||
'''[geometry] Returns a geometry representing the bounding box of a geometry.\nOR\n[raster] Returns the polygon representation of the extent of the raster.'''),
|
||||
('ST_BoundingDiagonal', types.Geometry,
|
||||
'''Returns the diagonal of a geometry's bounding box.'''),
|
||||
('ST_ExteriorRing', types.Geometry,
|
||||
'''Returns a LineString representing the exterior ring of a Polygon.'''),
|
||||
('ST_GeometryN', types.Geometry,
|
||||
'''Return the Nth geometry element of a geometry collection.'''),
|
||||
('ST_GeometryType', None,
|
||||
'''Returns the SQL-MM type of a geometry as text.'''),
|
||||
('ST_HasArc', None,
|
||||
'''Tests if a geometry contains a circular arc'''),
|
||||
('ST_InteriorRingN', types.Geometry,
|
||||
'''Returns the Nth interior ring (hole) of a Polygon.'''),
|
||||
('ST_IsPolygonCCW', None,
|
||||
'''Tests if Polygons have exterior rings oriented counter-clockwise and interior rings oriented clockwise.'''),
|
||||
('ST_IsPolygonCW', None,
|
||||
'''Tests if Polygons have exterior rings oriented clockwise and interior rings oriented counter-clockwise.'''),
|
||||
('ST_IsClosed', None,
|
||||
'''Tests if a LineStrings's start and end points are coincident. For a PolyhedralSurface tests if it is closed (volumetric).'''),
|
||||
('ST_IsCollection', None,
|
||||
'''Tests if a geometry is a geometry collection type.'''),
|
||||
('ST_IsEmpty', None,
|
||||
'''[geometry] Tests if a geometry is empty.\nOR\n[raster] Returns true if the raster is empty (width = 0 and height = 0). Otherwise, returns false.'''),
|
||||
('ST_IsRing', None,
|
||||
'''Tests if a LineString is closed and simple.'''),
|
||||
('ST_IsSimple', None,
|
||||
'''Tests if a geometry has no points of self-intersection or self-tangency.'''),
|
||||
('ST_M', None,
|
||||
'''Returns the M coordinate of a Point.'''),
|
||||
('ST_MemSize', None,
|
||||
'''[geometry] Returns the amount of memory space a geometry takes.\nOR\n[raster] Returns the amount of space (in bytes) the raster takes.'''),
|
||||
('ST_NDims', None,
|
||||
'''Returns the coordinate dimension of a geometry.'''),
|
||||
('ST_NPoints', None,
|
||||
'''Returns the number of points (vertices) in a geometry.'''),
|
||||
('ST_NRings', None,
|
||||
'''Returns the number of rings in a polygonal geometry.'''),
|
||||
('ST_NumGeometries', None,
|
||||
'''Returns the number of elements in a geometry collection.'''),
|
||||
('ST_NumInteriorRings', None,
|
||||
'''Returns the number of interior rings (holes) of a Polygon.'''),
|
||||
('ST_NumInteriorRing', None,
|
||||
'''Returns the number of interior rings (holes) of a Polygon. Aias for ST_NumInteriorRings'''),
|
||||
('ST_NumPatches', None,
|
||||
'''Return the number of faces on a Polyhedral Surface. Will return null for non-polyhedral geometries.'''),
|
||||
('ST_NumPoints', None,
|
||||
'''Returns the number of points in a LineString or CircularString.'''),
|
||||
('ST_PatchN', types.Geometry,
|
||||
'''Returns the Nth geometry (face) of a PolyhedralSurface.'''),
|
||||
('ST_PointN', types.Geometry,
|
||||
'''Returns the Nth point in the first LineString or circular LineString in a geometry.'''),
|
||||
('ST_Points', types.Geometry,
|
||||
'''Returns a MultiPoint containing all the coordinates of a geometry.'''),
|
||||
('ST_StartPoint', types.Geometry,
|
||||
'''Returns the first point of a LineString.'''),
|
||||
('ST_Summary', None,
|
||||
'''[geometry] Returns a text summary of the contents of a geometry.\nOR\n[raster] Returns a text summary of the contents of the raster.'''),
|
||||
('ST_X', None,
|
||||
'''Returns the X coordinate of a Point.'''),
|
||||
('ST_Y', None,
|
||||
'''Returns the Y coordinate of a Point.'''),
|
||||
('ST_Z', None,
|
||||
'''Returns the Z coordinate of a Point.'''),
|
||||
('ST_Zmflag', None,
|
||||
'''Returns a code indicating the ZM coordinate dimension of a geometry.'''),
|
||||
('ST_AddPoint', types.Geometry,
|
||||
'''Add a point to a LineString.'''),
|
||||
('ST_CollectionExtract', types.Geometry,
|
||||
'''Given a (multi)geometry, return a (multi)geometry consisting only of elements of the specified type.'''),
|
||||
('ST_CollectionHomogenize', types.Geometry,
|
||||
'''Given a geometry collection, return the \"simplest\" representation of the contents.'''),
|
||||
('ST_Force2D', types.Geometry,
|
||||
'''Force the geometries into a \"2-dimensional mode\".'''),
|
||||
('ST_Force3D', types.Geometry,
|
||||
('''Force the geometries into XYZ mode. This is an alias for ST_Force3DZ.''', 'ST_Force_3D')),
|
||||
('ST_Force3DZ', types.Geometry,
|
||||
('''Force the geometries into XYZ mode.''', 'ST_Force_3DZ')),
|
||||
('ST_Force3DM', types.Geometry,
|
||||
('''Force the geometries into XYM mode.''', 'ST_Force_3DZ')),
|
||||
('ST_Force4D', types.Geometry,
|
||||
('''Force the geometries into XYZM mode.''', 'ST_Force_4D')),
|
||||
('ST_ForcePolygonCCW', types.Geometry,
|
||||
'''Orients all exterior rings counter-clockwise and all interior rings clockwise.'''),
|
||||
('ST_ForceCollection', types.Geometry,
|
||||
('''Convert the geometry into a GEOMETRYCOLLECTION.''', 'ST_Force_Collection')),
|
||||
('ST_ForcePolygonCW', types.Geometry,
|
||||
'''Orients all exterior rings clockwise and all interior rings counter-clockwise.'''),
|
||||
('ST_ForceSFS', types.Geometry,
|
||||
'''Force the geometries to use SFS 1.1 geometry types only.'''),
|
||||
('ST_ForceRHR', types.Geometry,
|
||||
'''Force the orientation of the vertices in a polygon to follow the Right-Hand-Rule.'''),
|
||||
('ST_ForceCurve', types.Geometry,
|
||||
'''Upcast a geometry into its curved type, if applicable.'''),
|
||||
('ST_LineMerge', types.Geometry,
|
||||
'''Return a (set of) LineString(s) formed by sewing together a MULTILINESTRING.'''),
|
||||
('ST_Multi', types.Geometry,
|
||||
'''Return the geometry as a MULTI* geometry.'''),
|
||||
('ST_Normalize', types.Geometry,
|
||||
'''Return the geometry in its canonical form.'''),
|
||||
('ST_QuantizeCoordinates', types.Geometry,
|
||||
'''Sets least significant bits of coordinates to zero'''),
|
||||
('ST_RemovePoint', types.Geometry,
|
||||
'''Remove point from a linestring.'''),
|
||||
('ST_Reverse', types.Geometry,
|
||||
'''Return the geometry with vertex order reversed.'''),
|
||||
('ST_Segmentize', types.Geometry,
|
||||
'''Return a modified geometry/geography having no segment longer than the given distance.'''),
|
||||
('ST_SetPoint', types.Geometry,
|
||||
'''Replace point of a linestring with a given point.'''),
|
||||
('ST_SnapToGrid', types.Geometry,
|
||||
'''[geometry] Snap all points of the input geometry to a regular grid.\nOR\n[raster] Resample a raster by snapping it to a grid. New pixel values are computed using the NearestNeighbor (english or american spelling), Bilinear, Cubic, CubicSpline or Lanczos resampling algorithm. Default is NearestNeighbor.'''),
|
||||
('ST_Snap', types.Geometry,
|
||||
'''Snap segments and vertices of input geometry to vertices of a reference geometry.'''),
|
||||
('ST_SwapOrdinates', types.Geometry,
|
||||
'''Returns a version of the given geometry with given ordinate values swapped.'''),
|
||||
('ST_IsValid', None,
|
||||
'''Tests if a geometry is well-formed in 2D.'''),
|
||||
('ST_IsValidDetail', None,
|
||||
'''Returns a valid_detail row stating if a geometry is valid, and if not a reason why and a location.'''),
|
||||
('ST_IsValidReason', None,
|
||||
'''Returns text stating if a geometry is valid, or a reason for invalidity.'''),
|
||||
('ST_SetSRID', types.Geometry,
|
||||
'''[geometry] Set the SRID on a geometry to a particular integer value.\nOR\n[raster] Sets the SRID of a raster to a particular integer srid defined in the spatial_ref_sys table.'''),
|
||||
('ST_SRID', None,
|
||||
'''[geometry] Returns the spatial reference identifier for the ST_Geometry as defined in spatial_ref_sys table.\nOR\n[raster] Returns the spatial reference identifier of the raster as defined in spatial_ref_sys table.'''),
|
||||
('ST_Transform', types.Geometry,
|
||||
'''[geometry] Return a new geometry with its coordinates transformed to a different spatial reference system.\nOR\n[raster] Reprojects a raster in a known spatial reference system to another known spatial reference system using specified resampling algorithm. Options are NearestNeighbor, Bilinear, Cubic, CubicSpline, Lanczos defaulting to NearestNeighbor.'''),
|
||||
('ST_BdPolyFromText', types.Geometry,
|
||||
'''Construct a Polygon given an arbitrary collection of closed linestrings as a MultiLineString Well-Known text representation.'''),
|
||||
('ST_BdMPolyFromText', types.Geometry,
|
||||
'''Construct a MultiPolygon given an arbitrary collection of closed linestrings as a MultiLineString text representation Well-Known text representation.'''),
|
||||
('ST_GeogFromText', types.Geography,
|
||||
'''Return a specified geography value from Well-Known Text representation or extended (WKT).'''),
|
||||
('ST_GeographyFromText', types.Geography,
|
||||
'''Return a specified geography value from Well-Known Text representation or extended (WKT).'''),
|
||||
('ST_GeomCollFromText', types.Geometry,
|
||||
'''Makes a collection Geometry from collection WKT with the given SRID. If SRID is not given, it defaults to 0.'''),
|
||||
('ST_GeomFromEWKT', types.Geometry,
|
||||
'''Return a specified ST_Geometry value from Extended Well-Known Text representation (EWKT).'''),
|
||||
('ST_GeometryFromText', types.Geometry,
|
||||
'''Return a specified ST_Geometry value from Well-Known Text representation (WKT). This is an alias name for ST_GeomFromText'''),
|
||||
('ST_GeomFromText', types.Geometry,
|
||||
'''Return a specified ST_Geometry value from Well-Known Text representation (WKT).'''),
|
||||
('ST_LineFromText', types.Geometry,
|
||||
'''Makes a Geometry from WKT representation with the given SRID. If SRID is not given, it defaults to 0.'''),
|
||||
('ST_MLineFromText', types.Geometry,
|
||||
'''Return a specified ST_MultiLineString value from WKT representation.'''),
|
||||
('ST_MPointFromText', types.Geometry,
|
||||
'''Makes a Geometry from WKT with the given SRID. If SRID is not given, it defaults to 0.'''),
|
||||
('ST_MPolyFromText', types.Geometry,
|
||||
'''Makes a MultiPolygon Geometry from WKT with the given SRID. If SRID is not given, it defaults to 0.'''),
|
||||
('ST_PointFromText', types.Geometry,
|
||||
'''Makes a point Geometry from WKT with the given SRID. If SRID is not given, it defaults to unknown.'''),
|
||||
('ST_PolygonFromText', types.Geometry,
|
||||
'''Makes a Geometry from WKT with the given SRID. If SRID is not given, it defaults to 0.'''),
|
||||
('ST_WKTToSQL', types.Geometry,
|
||||
'''Return a specified ST_Geometry value from Well-Known Text representation (WKT). This is an alias name for ST_GeomFromText'''),
|
||||
('ST_GeogFromWKB', types.Geography,
|
||||
'''Creates a geography instance from a Well-Known Binary geometry representation (WKB) or extended Well Known Binary (EWKB).'''),
|
||||
('ST_GeomFromEWKB', types.Geometry,
|
||||
'''Return a specified ST_Geometry value from Extended Well-Known Binary representation (EWKB).'''),
|
||||
('ST_GeomFromWKB', types.Geometry,
|
||||
'''Creates a geometry instance from a Well-Known Binary geometry representation (WKB) and optional SRID.'''),
|
||||
('ST_LineFromWKB', types.Geometry,
|
||||
'''Makes a LINESTRING from WKB with the given SRID'''),
|
||||
('ST_LinestringFromWKB', types.Geometry,
|
||||
'''Makes a geometry from WKB with the given SRID.'''),
|
||||
('ST_PointFromWKB', types.Geometry,
|
||||
'''Makes a geometry from WKB with the given SRID'''),
|
||||
('ST_WKBToSQL', types.Geometry,
|
||||
'''Return a specified ST_Geometry value from Well-Known Binary representation (WKB). This is an alias name for ST_GeomFromWKB that takes no srid'''),
|
||||
('ST_Box2dFromGeoHash', None, # return an unsupported Box2d object
|
||||
'''Return a BOX2D from a GeoHash string.'''),
|
||||
('ST_GeomFromGeoHash', types.Geometry,
|
||||
'''Return a geometry from a GeoHash string.'''),
|
||||
('ST_GeomFromGML', types.Geometry,
|
||||
'''Takes as input GML representation of geometry and outputs a PostGIS geometry object'''),
|
||||
('ST_GeomFromGeoJSON', types.Geometry,
|
||||
'''Takes as input a geojson representation of a geometry and outputs a PostGIS geometry object'''),
|
||||
('ST_GeomFromKML', types.Geometry,
|
||||
'''Takes as input KML representation of geometry and outputs a PostGIS geometry object'''),
|
||||
('ST_GeomFromTWKB', types.Geometry,
|
||||
'''Creates a geometry instance from a TWKB (\"Tiny Well-Known Binary\") geometry representation.'''),
|
||||
('ST_GMLToSQL', types.Geometry,
|
||||
'''Return a specified ST_Geometry value from GML representation. This is an alias name for ST_GeomFromGML'''),
|
||||
('ST_LineFromEncodedPolyline', types.Geometry,
|
||||
'''Creates a LineString from an Encoded Polyline.'''),
|
||||
('ST_PointFromGeoHash', types.Geometry,
|
||||
'''Return a point from a GeoHash string.'''),
|
||||
('ST_AsEWKT', None,
|
||||
'''Return the Well-Known Text (WKT) representation of the geometry with SRID meta data.'''),
|
||||
('ST_AsText', None,
|
||||
'''Return the Well-Known Text (WKT) representation of the geometry/geography without SRID metadata.'''),
|
||||
('ST_AsBinary', None,
|
||||
'''[geometry] Return the Well-Known Binary (WKB) representation of the geometry/geography without SRID meta data.\nOR\n[raster] Return the Well-Known Binary (WKB) representation of the raster.'''),
|
||||
('ST_AsEWKB', None,
|
||||
'''Return the Well-Known Binary (WKB) representation of the geometry with SRID meta data.'''),
|
||||
('ST_AsHEXEWKB', None,
|
||||
'''Returns a Geometry in HEXEWKB format (as text) using either little-endian (NDR) or big-endian (XDR) encoding.'''),
|
||||
('ST_AsEncodedPolyline', None,
|
||||
'''Returns an Encoded Polyline from a LineString geometry.'''),
|
||||
('ST_AsGeobuf', None,
|
||||
'''Return a Geobuf representation of a set of rows.'''),
|
||||
('ST_AsGML', None,
|
||||
'''Return the geometry as a GML version 2 or 3 element.'''),
|
||||
('ST_AsKML', None,
|
||||
'''Return the geometry as a KML element. Several variants. Default version=2, default maxdecimaldigits=15'''),
|
||||
('ST_AsLatLonText', None,
|
||||
'''Return the Degrees, Minutes, Seconds representation of the given point.'''),
|
||||
('ST_AsMVTGeom', types.Geometry,
|
||||
'''Transform a geometry into the coordinate space of a Mapbox Vector Tile.'''),
|
||||
('ST_AsMVT', None,
|
||||
'''Aggregate function returning a Mapbox Vector Tile representation of a set of rows.'''),
|
||||
('ST_AsSVG', None,
|
||||
'''Returns SVG path data for a geometry.'''),
|
||||
('ST_AsTWKB', None,
|
||||
'''Returns the geometry as TWKB, aka \"Tiny Well-Known Binary\"'''),
|
||||
('ST_AsX3D', None,
|
||||
'''Returns a Geometry in X3D xml node element format: ISO-IEC-19776-1.2-X3DEncodings-XML'''),
|
||||
('ST_GeoHash', None,
|
||||
'''Return a GeoHash representation of the geometry.'''),
|
||||
('ST_3DIntersects', None,
|
||||
'''Returns TRUE if the Geometries \"spatially intersect\" in 3D - only for points, linestrings, polygons, polyhedral surface (area).'''),
|
||||
('ST_Contains', None,
|
||||
'''[geometry] Returns true if and only if no points of B lie in the exterior of A, and at least one point of the interior of B lies in the interior of A.\nOR\n[raster] Return true if no points of raster rastB lie in the exterior of raster rastA and at least one point of the interior of rastB lies in the interior of rastA.'''),
|
||||
('ST_ContainsProperly', None,
|
||||
'''[geometry] Returns true if B intersects the interior of A but not the boundary (or exterior). A does not contain properly itself, but does contain itself.\nOR\n[raster] Return true if rastB intersects the interior of rastA but not the boundary or exterior of rastA.'''),
|
||||
('ST_Covers', None,
|
||||
'''[geometry] Returns 1 (TRUE) if no point in Geometry B is outside Geometry A\nOR\n[raster] Return true if no points of raster rastB lie outside raster rastA.'''),
|
||||
('ST_CoveredBy', None,
|
||||
'''[geometry] Returns 1 (TRUE) if no point in Geometry/Geography A is outside Geometry/Geography B\nOR\n[raster] Return true if no points of raster rastA lie outside raster rastB.'''),
|
||||
('ST_Crosses', None,
|
||||
'''Returns TRUE if the supplied geometries have some, but not all, interior points in common.'''),
|
||||
('ST_LineCrossingDirection', None,
|
||||
'''Given 2 linestrings, returns a number between -3 and 3 denoting what kind of crossing behavior. 0 is no crossing.'''),
|
||||
('ST_Disjoint', None,
|
||||
'''[geometry] Returns TRUE if the Geometries do not \"spatially intersect\" - if they do not share any space together.\nOR\n[raster] Return true if raster rastA does not spatially intersect rastB.'''),
|
||||
('ST_Equals', None,
|
||||
'''Returns true if the given geometries represent the same geometry. Directionality is ignored.'''),
|
||||
('ST_Intersects', None,
|
||||
'''[geometry] Returns TRUE if the Geometries/Geography \"spatially intersect in 2D\" - (share any portion of space) and FALSE if they don't (they are Disjoint). For geography tolerance is 0.00001 meters (so any points that close are considered to intersect)\nOR\n[raster] Return true if raster rastA spatially intersects raster rastB.'''),
|
||||
('ST_OrderingEquals', None,
|
||||
'''Returns true if the given geometries represent the same geometry and points are in the same directional order.'''),
|
||||
('ST_Overlaps', None,
|
||||
'''[geometry] Returns TRUE if the Geometries share space, are of the same dimension, but are not completely contained by each other.\nOR\n[raster] Return true if raster rastA and rastB intersect but one does not completely contain the other.'''),
|
||||
('ST_PointInsideCircle', None,
|
||||
'''Is the point geometry inside the circle defined by center_x, center_y, radius'''),
|
||||
('ST_Relate', None,
|
||||
'''Returns true if this Geometry is spatially related to anotherGeometry, by testing for intersections between the Interior, Boundary and Exterior of the two geometries as specified by the values in the intersectionMatrixPattern. If no intersectionMatrixPattern is passed in, then returns the maximum intersectionMatrixPattern that relates the 2 geometries.'''),
|
||||
('ST_RelateMatch', None,
|
||||
'''Returns true if intersectionMattrixPattern1 implies intersectionMatrixPattern2'''),
|
||||
('ST_Touches', None,
|
||||
'''[geometry] Returns TRUE if the geometries have at least one point in common, but their interiors do not intersect.\nOR\n[raster] Return true if raster rastA and rastB have at least one point in common but their interiors do not intersect.'''),
|
||||
('ST_Within', None,
|
||||
'''[geometry] Returns true if the geometry A is completely inside geometry B\nOR\n[raster] Return true if no points of raster rastA lie in the exterior of raster rastB and at least one point of the interior of rastA lies in the interior of rastB.'''),
|
||||
('ST_3DDWithin', None,
|
||||
'''For 3d (z) geometry type Returns true if two geometries 3d distance is within number of units.'''),
|
||||
('ST_3DDFullyWithin', None,
|
||||
'''Returns true if all of the 3D geometries are within the specified distance of one another.'''),
|
||||
('ST_DFullyWithin', None,
|
||||
'''[geometry] Returns true if all of the geometries are within the specified distance of one another\nOR\n[raster] Return true if rasters rastA and rastB are fully within the specified distance of each other.'''),
|
||||
('ST_DWithin', None,
|
||||
'''[geometry] Returns true if the geometries are within the specified distance of one another. For geometry units are in those of spatial reference and for geography units are in meters and measurement is defaulted to use_spheroid=true (measure around spheroid), for faster check, use_spheroid=false to measure along sphere.\nOR\n[raster] Return true if rasters rastA and rastB are within the specified distance of each other.'''),
|
||||
('ST_Area', None,
|
||||
'''Returns the area of a polygonal geometry.'''),
|
||||
('ST_Azimuth', None,
|
||||
'''Returns the north-based azimuth as the angle in radians measured clockwise from the vertical on pointA to pointB.'''),
|
||||
('ST_Angle', None,
|
||||
'''Returns the angle between 3 points, or between 2 vectors (4 points or 2 lines).'''),
|
||||
('ST_ClosestPoint', types.Geometry,
|
||||
'''Returns the 2D point on g1 that is closest to g2. This is the first point of the shortest line.'''),
|
||||
('ST_3DClosestPoint', types.Geometry,
|
||||
'''Returns the 3D point on g1 that is closest to g2. This is the first point of the 3D shortest line.'''),
|
||||
('ST_Distance', None,
|
||||
'''Returns the distance between two geometry or geography values.'''),
|
||||
('ST_3DDistance', None,
|
||||
'''Returns the 3D cartesian minimum distance (based on spatial ref) between two geometries in projected units.'''),
|
||||
('ST_DistanceSphere', None,
|
||||
'''Returns minimum distance in meters between two lon/lat geometries using a spherical earth model.'''),
|
||||
('ST_DistanceSpheroid', None,
|
||||
'''Returns the minimum distance between two lon/lat geometries using a spheroidal earth model.'''),
|
||||
('ST_FrechetDistance', None,
|
||||
'''Returns the Fréchet distance between two geometries.'''),
|
||||
('ST_HausdorffDistance', None,
|
||||
'''Returns the Hausdorff distance between two geometries.'''),
|
||||
('ST_Length', None,
|
||||
'''Returns the 2D length of a linear geometry.'''),
|
||||
('ST_Length2D', None,
|
||||
'''Returns the 2D length of a linear geometry. Alias for ST_Length'''),
|
||||
('ST_3DLength', None,
|
||||
'''Returns the 3D length of a linear geometry.'''),
|
||||
('ST_LengthSpheroid', None,
|
||||
'''Returns the 2D or 3D length/perimeter of a lon/lat geometry on a spheroid.'''),
|
||||
('ST_LongestLine', types.Geometry,
|
||||
'''Returns the 2D longest line between two geometries.'''),
|
||||
('ST_3DLongestLine', types.Geometry,
|
||||
'''Returns the 3D longest line between two geometries'''),
|
||||
('ST_MaxDistance', None,
|
||||
'''Returns the 2D largest distance between two geometries in projected units.'''),
|
||||
('ST_3DMaxDistance', None,
|
||||
'''Returns the 3D cartesian maximum distance (based on spatial ref) between two geometries in projected units.'''),
|
||||
('ST_MinimumClearance', None,
|
||||
'''Returns the minimum clearance of a geometry, a measure of a geometry's robustness.'''),
|
||||
('ST_MinimumClearanceLine', types.Geometry,
|
||||
'''Returns the two-point LineString spanning a geometry's minimum clearance.'''),
|
||||
('ST_Perimeter', None,
|
||||
'''Returns the length of the boundary of a polygonal geometry or geography.'''),
|
||||
('ST_Perimeter2D', None,
|
||||
'''Returns the 2D perimeter of a polygonal geometry. Alias for ST_Perimeter.'''),
|
||||
('ST_3DPerimeter', None,
|
||||
'''Returns the 3D perimeter of a polygonal geometry.'''),
|
||||
('ST_Project', types.Geography,
|
||||
'''Returns a point projected from a start point by a distance and bearing (azimuth).'''),
|
||||
('ST_ShortestLine', types.Geometry,
|
||||
'''Returns the 2D shortest line between two geometries'''),
|
||||
('ST_3DShortestLine', types.Geometry,
|
||||
'''Returns the 3D shortest line between two geometries'''),
|
||||
('ST_Buffer', types.Geometry,
|
||||
'''(T) Returns a geometry covering all points within a given distance from the input geometry.'''),
|
||||
('ST_BuildArea', types.Geometry,
|
||||
'''Creates an areal geometry formed by the constituent linework of given geometry'''),
|
||||
('ST_Centroid', types.Geometry,
|
||||
'''Returns the geometric center of a geometry.'''),
|
||||
('ST_ClipByBox2D', types.Geometry,
|
||||
'''Returns the portion of a geometry falling within a rectangle.'''),
|
||||
('ST_ConcaveHull', types.Geometry,
|
||||
'''The concave hull of a geometry represents a possibly concave geometry that encloses all geometries within the set. You can think of it as shrink wrapping.'''),
|
||||
('ST_ConvexHull', types.Geometry,
|
||||
'''[geometry] Computes the convex hull of a geometry.\nOR\n[raster] Return the convex hull geometry of the raster including pixel values equal to BandNoDataValue. For regular shaped and non-skewed rasters, this gives the same result as ST_Envelope so only useful for irregularly shaped or skewed rasters.'''),
|
||||
('ST_CurveToLine', types.Geometry,
|
||||
'''Converts a CIRCULARSTRING/CURVEPOLYGON/MULTISURFACE to a LINESTRING/POLYGON/MULTIPOLYGON'''),
|
||||
('ST_DelaunayTriangles', types.Geometry,
|
||||
'''Return a Delaunay triangulation around the given input points.'''),
|
||||
('ST_Difference', types.Geometry,
|
||||
'''Returns a geometry that represents that part of geometry A that does not intersect with geometry B.'''),
|
||||
('ST_FlipCoordinates', types.Geometry,
|
||||
'''Returns a version of the given geometry with X and Y axis flipped. Useful for people who have built latitude/longitude features and need to fix them.'''),
|
||||
('ST_GeneratePoints', types.Geometry,
|
||||
'''Converts a polygon or multi-polygon into a multi-point composed of randomly location points within the original areas.'''),
|
||||
('ST_GeometricMedian', types.Geometry,
|
||||
'''Returns the geometric median of a MultiPoint.'''),
|
||||
('ST_Intersection', types.Geometry,
|
||||
'''[geometry] (T) Returns a geometry that represents the shared portion of geomA and geomB.\nOR\n[raster] Returns a raster or a set of geometry-pixelvalue pairs representing the shared portion of two rasters or the geometrical intersection of a vectorization of the raster and a geometry.'''),
|
||||
('ST_LineToCurve', types.Geometry,
|
||||
'''Converts a LINESTRING/POLYGON to a CIRCULARSTRING, CURVEPOLYGON'''),
|
||||
('ST_MakeValid', types.Geometry,
|
||||
'''Attempts to make an invalid geometry valid without losing vertices.'''),
|
||||
('ST_MemUnion', types.Geometry,
|
||||
'''Same as ST_Union, only memory-friendly (uses less memory and more processor time).'''),
|
||||
('ST_MinimumBoundingCircle', types.Geometry,
|
||||
'''Returns the smallest circle polygon that can fully contain a geometry. Default uses 48 segments per quarter circle.'''),
|
||||
('ST_MinimumBoundingRadius', None,
|
||||
'''Returns the center point and radius of the smallest circle that can fully contain a geometry.'''),
|
||||
('ST_OrientedEnvelope', types.Geometry,
|
||||
'''Returns a minimum rotated rectangle enclosing a geometry.'''),
|
||||
('ST_Polygonize', types.Geometry,
|
||||
'''Aggregate. Creates a GeometryCollection containing possible polygons formed from the constituent linework of a set of geometries.'''),
|
||||
('ST_Node', types.Geometry,
|
||||
'''Node a set of linestrings.'''),
|
||||
('ST_OffsetCurve', types.Geometry,
|
||||
'''Return an offset line at a given distance and side from an input line. Useful for computing parallel lines about a center line'''),
|
||||
('ST_PointOnSurface', types.Geometry,
|
||||
'''Returns a POINT guaranteed to lie on the surface.'''),
|
||||
('ST_RemoveRepeatedPoints', types.Geometry,
|
||||
'''Returns a version of the given geometry with duplicated points removed.'''),
|
||||
('ST_SharedPaths', types.Geometry,
|
||||
'''Returns a collection containing paths shared by the two input linestrings/multilinestrings.'''),
|
||||
('ST_ShiftLongitude', types.Geometry,
|
||||
('''Toggle geometry coordinates between -180..180 and 0..360 ranges.''', 'ST_Shift_Longitude')),
|
||||
('ST_WrapX', types.Geometry,
|
||||
'''Wrap a geometry around an X value.'''),
|
||||
('ST_Simplify', types.Geometry,
|
||||
'''Returns a \"simplified\" version of the given geometry using the Douglas-Peucker algorithm.'''),
|
||||
('ST_SimplifyPreserveTopology', types.Geometry,
|
||||
'''Returns a \"simplified\" version of the given geometry using the Douglas-Peucker algorithm. Will avoid creating derived geometries (polygons in particular) that are invalid.'''),
|
||||
('ST_SimplifyVW', types.Geometry,
|
||||
'''Returns a \"simplified\" version of the given geometry using the Visvalingam-Whyatt algorithm'''),
|
||||
('ST_ChaikinSmoothing', types.Geometry,
|
||||
'''Returns a \"smoothed\" version of the given geometry using the Chaikin algorithm'''),
|
||||
('ST_FilterByM', types.Geometry,
|
||||
'''Filters vertex points based on their m-value'''),
|
||||
('ST_SetEffectiveArea', types.Geometry,
|
||||
'''Sets the effective area for each vertex, storing the value in the M ordinate. A simplified geometry can then be generated by filtering on the M ordinate.'''),
|
||||
('ST_Split', types.Geometry,
|
||||
'''Returns a collection of geometries resulting by splitting a geometry.'''),
|
||||
('ST_SymDifference', types.Geometry,
|
||||
'''Returns a geometry that represents the portions of A and B that do not intersect. It is called a symmetric difference because ST_SymDifference(A,B) = ST_SymDifference(B,A).'''),
|
||||
('ST_Subdivide', types.Geometry,
|
||||
'''Returns a set of geometry where no geometry in the set has more than the specified number of vertices.'''),
|
||||
('ST_Union', types.Geometry,
|
||||
'''[geometry] Returns a geometry that represents the point set union of the Geometries.\nOR\n[raster] Returns the union of a set of raster tiles into a single raster composed of 1 or more bands.'''),
|
||||
('ST_UnaryUnion', types.Geometry,
|
||||
'''Like ST_Union, but working at the geometry component level.'''),
|
||||
('ST_VoronoiLines', types.Geometry,
|
||||
'''Returns the boundaries between the cells of the Voronoi diagram constructed from the vertices of a geometry.'''),
|
||||
('ST_VoronoiPolygons', types.Geometry,
|
||||
'''Returns the cells of the Voronoi diagram constructed from the vertices of a geometry.'''),
|
||||
('ST_Affine', types.Geometry,
|
||||
'''Apply a 3D affine transformation to a geometry.'''),
|
||||
('ST_Rotate', types.Geometry,
|
||||
'''Rotates a geometry about an origin point.'''),
|
||||
('ST_RotateX', types.Geometry,
|
||||
'''Rotates a geometry about the X axis.'''),
|
||||
('ST_RotateY', types.Geometry,
|
||||
'''Rotates a geometry about the Y axis.'''),
|
||||
('ST_RotateZ', types.Geometry,
|
||||
'''Rotates a geometry about the Z axis.'''),
|
||||
('ST_Scale', types.Geometry,
|
||||
'''Scales a geometry by given factors.'''),
|
||||
('ST_Translate', types.Geometry,
|
||||
'''Translates a geometry by given offsets.'''),
|
||||
('ST_TransScale', types.Geometry,
|
||||
'''Translates and scales a geometry by given offsets and factors.'''),
|
||||
('ST_ClusterDBSCAN', None,
|
||||
'''Window function that returns a cluster id for each input geometry using the DBSCAN algorithm.'''),
|
||||
('ST_ClusterIntersecting', types.Geometry,
|
||||
'''Aggregate function that clusters the input geometries into connected sets.'''),
|
||||
('ST_ClusterKMeans', None,
|
||||
'''Window function that returns a cluster id for each input geometry using the K-means algorithm.'''),
|
||||
('ST_ClusterWithin', types.Geometry,
|
||||
'''Aggregate function that clusters the input geometries by separation distance.'''),
|
||||
('Box2D', None, # return an unsupported Box2d object
|
||||
('''Returns a BOX2D representing the 2D extent of the geometry.''', 'Box2D_type')),
|
||||
('Box3D', None, # return an unsupported Box3d object
|
||||
('''[geometry] Returns a BOX3D representing the 3D extent of the geometry.\nOR\n[raster] Returns the box 3d representation of the enclosing box of the raster.''', 'Box3D_type')),
|
||||
('ST_EstimatedExtent', None, # return an unsupported Box2d object
|
||||
'''Return the 'estimated' extent of a spatial table.'''),
|
||||
('ST_Expand', types.Geometry,
|
||||
'''Returns a bounding box expanded from another bounding box or a geometry.'''),
|
||||
('ST_Extent', None, # return an unsupported Box2d object
|
||||
'''an aggregate function that returns the bounding box that bounds rows of geometries.'''),
|
||||
('ST_3DExtent', None, # return an unsupported Box3d object
|
||||
'''an aggregate function that returns the 3D bounding box that bounds rows of geometries.'''),
|
||||
('ST_MakeBox2D', None, # return an unsupported Box2d object
|
||||
'''Creates a BOX2D defined by two 2D point geometries.'''),
|
||||
('ST_3DMakeBox', None, # return an unsupported Box3d object
|
||||
'''Creates a BOX3D defined by two 3D point geometries.'''),
|
||||
('ST_XMax', None,
|
||||
'''Returns the X maxima of a 2D or 3D bounding box or a geometry.'''),
|
||||
('ST_XMin', None,
|
||||
'''Returns the X minima of a 2D or 3D bounding box or a geometry.'''),
|
||||
('ST_YMax', None,
|
||||
'''Returns the Y maxima of a 2D or 3D bounding box or a geometry.'''),
|
||||
('ST_YMin', None,
|
||||
'''Returns the Y minima of a 2D or 3D bounding box or a geometry.'''),
|
||||
('ST_ZMax', None,
|
||||
'''Returns the Z maxima of a 2D or 3D bounding box or a geometry.'''),
|
||||
('ST_ZMin', None,
|
||||
'''Returns the Z minima of a 2D or 3D bounding box or a geometry.'''),
|
||||
('ST_LineInterpolatePoint', types.Geometry,
|
||||
'''Returns a point interpolated along a line. Second argument is a float8 between 0 and 1 representing fraction of total length of linestring the point has to be located.'''),
|
||||
('ST_3DLineInterpolatePoint', types.Geometry,
|
||||
'''Returns a point interpolated along a line in 3D. Second argument is a float8 between 0 and 1 representing fraction of total length of linestring the point has to be located.'''),
|
||||
('ST_LineInterpolatePoints', types.Geometry,
|
||||
'''Returns one or more points interpolated along a line.'''),
|
||||
('ST_LineLocatePoint', None,
|
||||
'''Returns a float between 0 and 1 representing the location of the closest point on LineString to the given Point, as a fraction of total 2d line length.'''),
|
||||
('ST_LineSubstring', types.Geometry,
|
||||
'''Return a linestring being a substring of the input one starting and ending at the given fractions of total 2d length. Second and third arguments are float8 values between 0 and 1.'''),
|
||||
('ST_LocateAlong', types.Geometry,
|
||||
'''Return a derived geometry collection value with elements that match the specified measure. Polygonal elements are not supported.'''),
|
||||
('ST_LocateBetween', types.Geometry,
|
||||
'''Return a derived geometry collection value with elements that match the specified range of measures inclusively.'''),
|
||||
('ST_LocateBetweenElevations', types.Geometry,
|
||||
'''Return a derived geometry (collection) value with elements that intersect the specified range of elevations inclusively.'''),
|
||||
('ST_InterpolatePoint', None,
|
||||
'''Return the value of the measure dimension of a geometry at the point closed to the provided point.'''),
|
||||
('ST_AddMeasure', types.Geometry,
|
||||
'''Return a derived geometry with measure elements linearly interpolated between the start and end points.'''),
|
||||
('ST_IsValidTrajectory', None,
|
||||
'''Returns true if the geometry is a valid trajectory.'''),
|
||||
('ST_ClosestPointOfApproach', None,
|
||||
'''Returns the measure at which points interpolated along two trajectories are closest.'''),
|
||||
('ST_DistanceCPA', None,
|
||||
'''Returns the distance between the closest point of approach of two trajectories.'''),
|
||||
('ST_CPAWithin', None,
|
||||
'''Returns true if the closest point of approach of two trajectories is within the specified distance.'''),
|
||||
('postgis_sfcgal_version', None,
|
||||
'''Returns the version of SFCGAL in use'''),
|
||||
('ST_Extrude', types.Geometry,
|
||||
'''Extrude a surface to a related volume'''),
|
||||
('ST_StraightSkeleton', types.Geometry,
|
||||
'''Compute a straight skeleton from a geometry'''),
|
||||
('ST_ApproximateMedialAxis', types.Geometry,
|
||||
'''Compute the approximate medial axis of an areal geometry.'''),
|
||||
('ST_IsPlanar', None,
|
||||
'''Check if a surface is or not planar'''),
|
||||
('ST_Orientation', None,
|
||||
'''Determine surface orientation'''),
|
||||
('ST_ForceLHR', types.Geometry,
|
||||
'''Force LHR orientation'''),
|
||||
('ST_MinkowskiSum', types.Geometry,
|
||||
'''Performs Minkowski sum'''),
|
||||
('ST_ConstrainedDelaunayTriangles', types.Geometry,
|
||||
'''Return a constrained Delaunay triangulation around the given input geometry.'''),
|
||||
('ST_3DIntersection', types.Geometry,
|
||||
'''Perform 3D intersection'''),
|
||||
('ST_3DDifference', types.Geometry,
|
||||
'''Perform 3D difference'''),
|
||||
('ST_3DUnion', types.Geometry,
|
||||
'''Perform 3D union'''),
|
||||
('ST_3DArea', None,
|
||||
'''Computes area of 3D surface geometries. Will return 0 for solids.'''),
|
||||
('ST_Tesselate', types.Geometry,
|
||||
'''Perform surface Tessellation of a polygon or polyhedralsurface and returns as a TIN or collection of TINS'''),
|
||||
('ST_Volume', None,
|
||||
'''Computes the volume of a 3D solid. If applied to surface (even closed) geometries will return 0.'''),
|
||||
('ST_MakeSolid', types.Geometry,
|
||||
'''Cast the geometry into a solid. No check is performed. To obtain a valid solid, the input geometry must be a closed Polyhedral Surface or a closed TIN.'''),
|
||||
('ST_IsSolid', None,
|
||||
'''Test if the geometry is a solid. No validity check is performed.'''),
|
||||
('AddAuth', None,
|
||||
'''Adds an authorization token to be used in the current transaction.'''),
|
||||
('CheckAuth', None,
|
||||
'''Creates a trigger on a table to prevent/allow updates and deletes of rows based on authorization token.'''),
|
||||
('DisableLongTransactions', None,
|
||||
'''Disables long transaction support.'''),
|
||||
('EnableLongTransactions', None,
|
||||
'''Enables long transaction support.'''),
|
||||
('LockRow', None,
|
||||
'''Sets lock/authorization for a row in a table.'''),
|
||||
('UnlockRows', None,
|
||||
'''Removes all locks held by an authorization token.'''),
|
||||
('PostGIS_Extensions_Upgrade', None,
|
||||
'''Packages and upgrades postgis extensions (e.g. postgis_raster, postgis_topology, postgis_sfcgal) to latest available version.'''),
|
||||
('PostGIS_Full_Version', None,
|
||||
'''Reports full postgis version and build configuration infos.'''),
|
||||
('PostGIS_GEOS_Version', None,
|
||||
'''Returns the version number of the GEOS library.'''),
|
||||
('PostGIS_Liblwgeom_Version', None,
|
||||
'''Returns the version number of the liblwgeom library. This should match the version of PostGIS.'''),
|
||||
('PostGIS_LibXML_Version', None,
|
||||
'''Returns the version number of the libxml2 library.'''),
|
||||
('PostGIS_Lib_Build_Date', None,
|
||||
'''Returns build date of the PostGIS library.'''),
|
||||
('PostGIS_Lib_Version', None,
|
||||
'''Returns the version number of the PostGIS library.'''),
|
||||
('PostGIS_PROJ_Version', None,
|
||||
'''Returns the version number of the PROJ4 library.'''),
|
||||
('PostGIS_Wagyu_Version', None,
|
||||
'''Returns the version number of the internal Wagyu library.'''),
|
||||
('PostGIS_Scripts_Build_Date', None,
|
||||
'''Returns build date of the PostGIS scripts.'''),
|
||||
('PostGIS_Scripts_Installed', None,
|
||||
'''Returns version of the postgis scripts installed in this database.'''),
|
||||
('PostGIS_Scripts_Released', None,
|
||||
'''Returns the version number of the postgis.sql script released with the installed postgis lib.'''),
|
||||
('PostGIS_Version', None,
|
||||
'''Returns PostGIS version number and compile-time options.'''),
|
||||
('PostGIS_AddBBox', types.Geometry,
|
||||
'''Add bounding box to the geometry.'''),
|
||||
('PostGIS_DropBBox', types.Geometry,
|
||||
'''Drop the bounding box cache from the geometry.'''),
|
||||
('PostGIS_HasBBox', None,
|
||||
'''Returns TRUE if the bbox of this geometry is cached, FALSE otherwise.'''),
|
||||
('ST_AddBand', types.Raster,
|
||||
('''Returns a raster with the new band(s) of given type added with given initial value in the given index location. If no index is specified, the band is added to the end.''', 'RT_ST_AddBand')),
|
||||
('ST_AsRaster', types.Raster,
|
||||
('''Converts a PostGIS geometry to a PostGIS raster.''', 'RT_ST_AsRaster')),
|
||||
('ST_Band', types.Raster,
|
||||
('''Returns one or more bands of an existing raster as a new raster. Useful for building new rasters from existing rasters.''', 'RT_ST_Band')),
|
||||
('ST_MakeEmptyCoverage', types.Raster,
|
||||
('''Cover georeferenced area with a grid of empty raster tiles.''', 'RT_ST_MakeEmptyCoverage')),
|
||||
('ST_MakeEmptyRaster', types.Raster,
|
||||
('''Returns an empty raster (having no bands) of given dimensions (width & height), upperleft X and Y, pixel size and rotation (scalex, scaley, skewx & skewy) and reference system (srid). If a raster is passed in, returns a new raster with the same size, alignment and SRID. If srid is left out, the spatial ref is set to unknown (0).''', 'RT_ST_MakeEmptyRaster')),
|
||||
('ST_Tile', types.Raster,
|
||||
('''Returns a set of rasters resulting from the split of the input raster based upon the desired dimensions of the output rasters.''', 'RT_ST_Tile')),
|
||||
('ST_Retile', types.Raster,
|
||||
('''Return a set of configured tiles from an arbitrarily tiled raster coverage.''', 'RT_ST_Retile')),
|
||||
('ST_FromGDALRaster', types.Raster,
|
||||
('''Returns a raster from a supported GDAL raster file.''', 'RT_ST_FromGDALRaster')),
|
||||
('ST_GeoReference', None,
|
||||
('''Returns the georeference meta data in GDAL or ESRI format as commonly seen in a world file. Default is GDAL.''', 'RT_ST_GeoReference')),
|
||||
('ST_Height', None,
|
||||
('''Returns the height of the raster in pixels.''', 'RT_ST_Height')),
|
||||
('ST_MetaData', None,
|
||||
('''Returns basic meta data about a raster object such as pixel size, rotation (skew), upper, lower left, etc.''', 'RT_ST_MetaData')),
|
||||
('ST_NumBands', None,
|
||||
('''Returns the number of bands in the raster object.''', 'RT_ST_NumBands')),
|
||||
('ST_PixelHeight', None,
|
||||
('''Returns the pixel height in geometric units of the spatial reference system.''', 'RT_ST_PixelHeight')),
|
||||
('ST_PixelWidth', None,
|
||||
('''Returns the pixel width in geometric units of the spatial reference system.''', 'RT_ST_PixelWidth')),
|
||||
('ST_ScaleX', None,
|
||||
('''Returns the X component of the pixel width in units of coordinate reference system.''', 'RT_ST_ScaleX')),
|
||||
('ST_ScaleY', None,
|
||||
('''Returns the Y component of the pixel height in units of coordinate reference system.''', 'RT_ST_ScaleY')),
|
||||
('ST_RasterToWorldCoord', None,
|
||||
('''Returns the raster's upper left corner as geometric X and Y (longitude and latitude) given a column and row. Column and row starts at 1.''', 'RT_ST_RasterToWorldCoord')),
|
||||
('ST_RasterToWorldCoordX', None,
|
||||
('''Returns the geometric X coordinate upper left of a raster, column and row. Numbering of columns and rows starts at 1.''', 'RT_ST_RasterToWorldCoordX')),
|
||||
('ST_RasterToWorldCoordY', None,
|
||||
('''Returns the geometric Y coordinate upper left corner of a raster, column and row. Numbering of columns and rows starts at 1.''', 'RT_ST_RasterToWorldCoordY')),
|
||||
('ST_Rotation', None,
|
||||
('''Returns the rotation of the raster in radian.''', 'RT_ST_Rotation')),
|
||||
('ST_SkewX', None,
|
||||
('''Returns the georeference X skew (or rotation parameter).''', 'RT_ST_SkewX')),
|
||||
('ST_SkewY', None,
|
||||
('''Returns the georeference Y skew (or rotation parameter).''', 'RT_ST_SkewY')),
|
||||
('ST_UpperLeftX', None,
|
||||
('''Returns the upper left X coordinate of raster in projected spatial ref.''', 'RT_ST_UpperLeftX')),
|
||||
('ST_UpperLeftY', None,
|
||||
('''Returns the upper left Y coordinate of raster in projected spatial ref.''', 'RT_ST_UpperLeftY')),
|
||||
('ST_Width', None,
|
||||
('''Returns the width of the raster in pixels.''', 'RT_ST_Width')),
|
||||
('ST_WorldToRasterCoord', None,
|
||||
('''Returns the upper left corner as column and row given geometric X and Y (longitude and latitude) or a point geometry expressed in the spatial reference coordinate system of the raster.''', 'RT_ST_WorldToRasterCoord')),
|
||||
('ST_WorldToRasterCoordX', None,
|
||||
('''Returns the column in the raster of the point geometry (pt) or a X and Y world coordinate (xw, yw) represented in world spatial reference system of raster.''', 'RT_ST_WorldToRasterCoordX')),
|
||||
('ST_WorldToRasterCoordY', None,
|
||||
('''Returns the row in the raster of the point geometry (pt) or a X and Y world coordinate (xw, yw) represented in world spatial reference system of raster.''', 'RT_ST_WorldToRasterCoordY')),
|
||||
('ST_BandMetaData', None,
|
||||
('''Returns basic meta data for a specific raster band. band num 1 is assumed if none-specified.''', 'RT_ST_BandMetaData')),
|
||||
('ST_BandNoDataValue', None,
|
||||
('''Returns the value in a given band that represents no data. If no band num 1 is assumed.''', 'RT_ST_BandNoDataValue')),
|
||||
('ST_BandIsNoData', None,
|
||||
('''Returns true if the band is filled with only nodata values.''', 'RT_ST_BandIsNoData')),
|
||||
('ST_BandPath', None,
|
||||
('''Returns system file path to a band stored in file system. If no bandnum specified, 1 is assumed.''', 'RT_ST_BandPath')),
|
||||
('ST_BandFileSize', None,
|
||||
('''Returns the file size of a band stored in file system. If no bandnum specified, 1 is assumed.''', 'RT_ST_BandFileSize')),
|
||||
('ST_BandFileTimestamp', None,
|
||||
('''Returns the file timestamp of a band stored in file system. If no bandnum specified, 1 is assumed.''', 'RT_ST_BandFileTimestamp')),
|
||||
('ST_BandPixelType', None,
|
||||
('''Returns the type of pixel for given band. If no bandnum specified, 1 is assumed.''', 'RT_ST_BandPixelType')),
|
||||
('ST_MinPossibleValue', None,
|
||||
'''Returns the minimum value this pixeltype can store.'''),
|
||||
('ST_HasNoBand', None,
|
||||
('''Returns true if there is no band with given band number. If no band number is specified, then band number 1 is assumed.''', 'RT_ST_HasNoBand')),
|
||||
('ST_PixelAsPolygon', types.Geometry,
|
||||
('''Returns the polygon geometry that bounds the pixel for a particular row and column.''', 'RT_ST_PixelAsPolygon')),
|
||||
('ST_PixelAsPolygons', None,
|
||||
('''Returns the polygon geometry that bounds every pixel of a raster band along with the value, the X and the Y raster coordinates of each pixel.''', 'RT_ST_PixelAsPolygons')),
|
||||
('ST_PixelAsPoint', types.Geometry,
|
||||
('''Returns a point geometry of the pixel's upper-left corner.''', 'RT_ST_PixelAsPoint')),
|
||||
('ST_PixelAsPoints', None,
|
||||
('''Returns a point geometry for each pixel of a raster band along with the value, the X and the Y raster coordinates of each pixel. The coordinates of the point geometry are of the pixel's upper-left corner.''', 'RT_ST_PixelAsPoints')),
|
||||
('ST_PixelAsCentroid', types.Geometry,
|
||||
('''Returns the centroid (point geometry) of the area represented by a pixel.''', 'RT_ST_PixelAsCentroid')),
|
||||
('ST_PixelAsCentroids', None,
|
||||
('''Returns the centroid (point geometry) for each pixel of a raster band along with the value, the X and the Y raster coordinates of each pixel. The point geometry is the centroid of the area represented by a pixel.''', 'RT_ST_PixelAsCentroids')),
|
||||
('ST_Value', None,
|
||||
('''Returns the value of a given band in a given columnx, rowy pixel or at a particular geometric point. Band numbers start at 1 and assumed to be 1 if not specified. If exclude_nodata_value is set to false, then all pixels include nodata pixels are considered to intersect and return value. If exclude_nodata_value is not passed in then reads it from metadata of raster.''', 'RT_ST_Value')),
|
||||
('ST_NearestValue', None,
|
||||
('''Returns the nearest non-NODATA value of a given band's pixel specified by a columnx and rowy or a geometric point expressed in the same spatial reference coordinate system as the raster.''', 'RT_ST_NearestValue')),
|
||||
('ST_Neighborhood', None,
|
||||
('''Returns a 2-D double precision array of the non-NODATA values around a given band's pixel specified by either a columnX and rowY or a geometric point expressed in the same spatial reference coordinate system as the raster.''', 'RT_ST_Neighborhood')),
|
||||
('ST_SetValue', types.Raster,
|
||||
('''Returns modified raster resulting from setting the value of a given band in a given columnx, rowy pixel or the pixels that intersect a particular geometry. Band numbers start at 1 and assumed to be 1 if not specified.''', 'RT_ST_SetValue')),
|
||||
('ST_SetValues', types.Raster,
|
||||
('''Returns modified raster resulting from setting the values of a given band.''', 'RT_ST_SetValues')),
|
||||
('ST_DumpValues', None,
|
||||
('''Get the values of the specified band as a 2-dimension array.''', 'RT_ST_DumpValues')),
|
||||
('ST_PixelOfValue', None,
|
||||
('''Get the columnx, rowy coordinates of the pixel whose value equals the search value.''', 'RT_ST_PixelOfValue')),
|
||||
('ST_SetGeoReference', types.Raster,
|
||||
('''Set Georeference 6 georeference parameters in a single call. Numbers should be separated by white space. Accepts inputs in GDAL or ESRI format. Default is GDAL.''', 'RT_ST_SetGeoReference')),
|
||||
('ST_SetRotation', types.Raster,
|
||||
('''Set the rotation of the raster in radian.''', 'RT_ST_SetRotation')),
|
||||
('ST_SetScale', types.Raster,
|
||||
('''Sets the X and Y size of pixels in units of coordinate reference system. Number units/pixel width/height.''', 'RT_ST_SetScale')),
|
||||
('ST_SetSkew', types.Raster,
|
||||
('''Sets the georeference X and Y skew (or rotation parameter). If only one is passed in, sets X and Y to the same value.''', 'RT_ST_SetSkew')),
|
||||
('ST_SetUpperLeft', types.Raster,
|
||||
('''Sets the value of the upper left corner of the pixel of the raster to projected X and Y coordinates.''', 'RT_ST_SetUpperLeft')),
|
||||
('ST_Resample', types.Raster,
|
||||
('''Resample a raster using a specified resampling algorithm, new dimensions, an arbitrary grid corner and a set of raster georeferencing attributes defined or borrowed from another raster.''', 'RT_ST_Resample')),
|
||||
('ST_Rescale', types.Raster,
|
||||
('''Resample a raster by adjusting only its scale (or pixel size). New pixel values are computed using the NearestNeighbor (english or american spelling), Bilinear, Cubic, CubicSpline or Lanczos resampling algorithm. Default is NearestNeighbor.''', 'RT_ST_Rescale')),
|
||||
('ST_Reskew', types.Raster,
|
||||
('''Resample a raster by adjusting only its skew (or rotation parameters). New pixel values are computed using the NearestNeighbor (english or american spelling), Bilinear, Cubic, CubicSpline or Lanczos resampling algorithm. Default is NearestNeighbor.''', 'RT_ST_Reskew')),
|
||||
('ST_Resize', types.Raster,
|
||||
('''Resize a raster to a new width/height''', 'RT_ST_Resize')),
|
||||
('ST_SetBandNoDataValue', types.Raster,
|
||||
('''Sets the value for the given band that represents no data. Band 1 is assumed if no band is specified. To mark a band as having no nodata value, set the nodata value = NULL.''', 'RT_ST_SetBandNoDataValue')),
|
||||
('ST_SetBandIsNoData', types.Raster,
|
||||
('''Sets the isnodata flag of the band to TRUE.''', 'RT_ST_SetBandIsNoData')),
|
||||
('ST_SetBandPath', types.Raster,
|
||||
('''Update the external path and band number of an out-db band''', 'RT_ST_SetBandPath')),
|
||||
('ST_SetBandIndex', types.Raster,
|
||||
('''Update the external band number of an out-db band''', 'RT_ST_SetBandIndex')),
|
||||
('ST_Count', None,
|
||||
('''Returns the number of pixels in a given band of a raster or raster coverage. If no band is specified defaults to band 1. If exclude_nodata_value is set to true, will only count pixels that are not equal to the nodata value.''', 'RT_ST_Count')),
|
||||
('ST_CountAgg', None,
|
||||
('''Aggregate. Returns the number of pixels in a given band of a set of rasters. If no band is specified defaults to band 1. If exclude_nodata_value is set to true, will only count pixels that are not equal to the NODATA value.''', 'RT_ST_CountAgg')),
|
||||
('ST_Histogram', None,
|
||||
('''Returns a set of record summarizing a raster or raster coverage data distribution separate bin ranges. Number of bins are autocomputed if not specified.''', 'RT_ST_Histogram')),
|
||||
('ST_Quantile', None,
|
||||
('''Compute quantiles for a raster or raster table coverage in the context of the sample or population. Thus, a value could be examined to be at the raster's 25%, 50%, 75% percentile.''', 'RT_ST_Quantile')),
|
||||
('ST_SummaryStats', None,
|
||||
('''Returns summarystats consisting of count, sum, mean, stddev, min, max for a given raster band of a raster or raster coverage. Band 1 is assumed is no band is specified.''', 'RT_ST_SummaryStats')),
|
||||
('ST_SummaryStatsAgg', types.SummaryStats,
|
||||
('''Aggregate. Returns summarystats consisting of count, sum, mean, stddev, min, max for a given raster band of a set of raster. Band 1 is assumed is no band is specified.''', 'RT_ST_SummaryStatsAgg')),
|
||||
('ST_ValueCount', None,
|
||||
('''Returns a set of records containing a pixel band value and count of the number of pixels in a given band of a raster (or a raster coverage) that have a given set of values. If no band is specified defaults to band 1. By default nodata value pixels are not counted. and all other values in the pixel are output and pixel band values are rounded to the nearest integer.''', 'RT_ST_ValueCount')),
|
||||
('ST_RastFromWKB', types.Raster,
|
||||
('''Return a raster value from a Well-Known Binary (WKB) raster.''', 'RT_ST_RastFromWKB')),
|
||||
('ST_RastFromHexWKB', types.Raster,
|
||||
('''Return a raster value from a Hex representation of Well-Known Binary (WKB) raster.''', 'RT_ST_RastFromHexWKB')),
|
||||
('ST_AsWKB', None,
|
||||
('''Return the Well-Known Binary (WKB) representation of the raster.''', 'RT_ST_AsBinary')),
|
||||
('ST_AsHexWKB', None,
|
||||
('''Return the Well-Known Binary (WKB) in Hex representation of the raster.''', 'RT_ST_AsHexWKB')),
|
||||
('ST_AsGDALRaster', None,
|
||||
('''Return the raster tile in the designated GDAL Raster format. Raster formats are one of those supported by your compiled library. Use ST_GDALDrivers() to get a list of formats supported by your library.''', 'RT_ST_AsGDALRaster')),
|
||||
('ST_AsJPEG', None,
|
||||
('''Return the raster tile selected bands as a single Joint Photographic Exports Group (JPEG) image (byte array). If no band is specified and 1 or more than 3 bands, then only the first band is used. If only 3 bands then all 3 bands are used and mapped to RGB.''', 'RT_ST_AsJPEG')),
|
||||
('ST_AsPNG', None,
|
||||
('''Return the raster tile selected bands as a single portable network graphics (PNG) image (byte array). If 1, 3, or 4 bands in raster and no bands are specified, then all bands are used. If more 2 or more than 4 bands and no bands specified, then only band 1 is used. Bands are mapped to RGB or RGBA space.''', 'RT_ST_AsPNG')),
|
||||
('ST_AsTIFF', None,
|
||||
('''Return the raster selected bands as a single TIFF image (byte array). If no band is specified or any of specified bands does not exist in the raster, then will try to use all bands.''', 'RT_ST_AsTIFF')),
|
||||
('ST_Clip', types.Raster,
|
||||
('''Returns the raster clipped by the input geometry. If band number not is specified, all bands are processed. If crop is not specified or TRUE, the output raster is cropped.''', 'RT_ST_Clip')),
|
||||
('ST_ColorMap', types.Raster,
|
||||
('''Creates a new raster of up to four 8BUI bands (grayscale, RGB, RGBA) from the source raster and a specified band. Band 1 is assumed if not specified.''', 'RT_ST_ColorMap')),
|
||||
('ST_Grayscale', types.Raster,
|
||||
('''Creates a new one-8BUI band raster from the source raster and specified bands representing Red, Green and Blue''', 'RT_ST_Grayscale')),
|
||||
('ST_MapAlgebra', None,
|
||||
('''[raster] Callback function version - Returns a one-band raster given one or more input rasters, band indexes and one user-specified callback function.\nOR\n[raster] Expression version - Returns a one-band raster given one or two input rasters, band indexes and one or more user-specified SQL expressions.''', 'RT_ST_MapAlgebra')),
|
||||
('ST_MapAlgebraExpr', types.Raster,
|
||||
('''[raster] 1 raster band version: Creates a new one band raster formed by applying a valid PostgreSQL algebraic operation on the input raster band and of pixeltype provided. Band 1 is assumed if no band is specified.\nOR\n[raster] 2 raster band version: Creates a new one band raster formed by applying a valid PostgreSQL algebraic operation on the two input raster bands and of pixeltype provided. band 1 of each raster is assumed if no band numbers are specified. The resulting raster will be aligned (scale, skew and pixel corners) on the grid defined by the first raster and have its extent defined by the \"extenttype\" parameter. Values for \"extenttype\" can be: INTERSECTION, UNION, FIRST, SECOND.''', 'RT_ST_MapAlgebraExpr')),
|
||||
('ST_MapAlgebraFct', types.Raster,
|
||||
('''[raster] 1 band version - Creates a new one band raster formed by applying a valid PostgreSQL function on the input raster band and of pixeltype provided. Band 1 is assumed if no band is specified.\nOR\n[raster] 2 band version - Creates a new one band raster formed by applying a valid PostgreSQL function on the 2 input raster bands and of pixeltype provided. Band 1 is assumed if no band is specified. Extent type defaults to INTERSECTION if not specified.''', 'RT_ST_MapAlgebraFct')),
|
||||
('ST_MapAlgebraFctNgb', types.Raster,
|
||||
('''1-band version: Map Algebra Nearest Neighbor using user-defined PostgreSQL function. Return a raster which values are the result of a PLPGSQL user function involving a neighborhood of values from the input raster band.''', 'RT_ST_MapAlgebraFctNgb')),
|
||||
('ST_Reclass', types.Raster,
|
||||
('''Creates a new raster composed of band types reclassified from original. The nband is the band to be changed. If nband is not specified assumed to be 1. All other bands are returned unchanged. Use case: convert a 16BUI band to a 8BUI and so forth for simpler rendering as viewable formats.''', 'RT_ST_Reclass')),
|
||||
('ST_Distinct4ma', None,
|
||||
('''Raster processing function that calculates the number of unique pixel values in a neighborhood.''', 'RT_ST_Distinct4ma')),
|
||||
('ST_InvDistWeight4ma', None,
|
||||
('''Raster processing function that interpolates a pixel's value from the pixel's neighborhood.''', 'RT_ST_InvDistWeight4ma')),
|
||||
('ST_Max4ma', None,
|
||||
('''Raster processing function that calculates the maximum pixel value in a neighborhood.''', 'RT_ST_Max4ma')),
|
||||
('ST_Mean4ma', None,
|
||||
('''Raster processing function that calculates the mean pixel value in a neighborhood.''', 'RT_ST_Mean4ma')),
|
||||
('ST_Min4ma', None,
|
||||
('''Raster processing function that calculates the minimum pixel value in a neighborhood.''', 'RT_ST_Min4ma')),
|
||||
('ST_MinDist4ma', None,
|
||||
('''Raster processing function that returns the minimum distance (in number of pixels) between the pixel of interest and a neighboring pixel with value.''', 'RT_ST_MinDist4ma')),
|
||||
('ST_Range4ma', None,
|
||||
('''Raster processing function that calculates the range of pixel values in a neighborhood.''', 'RT_ST_Range4ma')),
|
||||
('ST_StdDev4ma', None,
|
||||
('''Raster processing function that calculates the standard deviation of pixel values in a neighborhood.''', 'RT_ST_StdDev4ma')),
|
||||
('ST_Sum4ma', None,
|
||||
('''Raster processing function that calculates the sum of all pixel values in a neighborhood.''', 'RT_ST_Sum4ma')),
|
||||
('ST_Aspect', types.Raster,
|
||||
('''Returns the aspect (in degrees by default) of an elevation raster band. Useful for analyzing terrain.''', 'RT_ST_Aspect')),
|
||||
('ST_HillShade', types.Raster,
|
||||
('''Returns the hypothetical illumination of an elevation raster band using provided azimuth, altitude, brightness and scale inputs.''', 'RT_ST_HillShade')),
|
||||
('ST_Roughness', types.Raster,
|
||||
('''Returns a raster with the calculated \"roughness\" of a DEM.''', 'RT_ST_Roughness')),
|
||||
('ST_Slope', types.Raster,
|
||||
('''Returns the slope (in degrees by default) of an elevation raster band. Useful for analyzing terrain.''', 'RT_ST_Slope')),
|
||||
('ST_TPI', types.Raster,
|
||||
('''Returns a raster with the calculated Topographic Position Index.''', 'RT_ST_TPI')),
|
||||
('ST_TRI', types.Raster,
|
||||
('''Returns a raster with the calculated Terrain Ruggedness Index.''', 'RT_ST_TRI')),
|
||||
('ST_DumpAsPolygons', None,
|
||||
('''Returns a set of geomval (geom,val) rows, from a given raster band. If no band number is specified, band num defaults to 1.''', 'RT_ST_DumpAsPolygons')),
|
||||
('ST_MinConvexHull', types.Geometry,
|
||||
('''Return the convex hull geometry of the raster excluding NODATA pixels.''', 'RT_ST_MinConvexHull')),
|
||||
('ST_SameAlignment', None,
|
||||
('''Returns true if rasters have same skew, scale, spatial ref, and offset (pixels can be put on same grid without cutting into pixels) and false if they don't with notice detailing issue.''', 'RT_ST_SameAlignment')),
|
||||
('ST_NotSameAlignmentReason', None,
|
||||
('''Returns text stating if rasters are aligned and if not aligned, a reason why.''', 'RT_ST_NotSameAlignmentReason')),
|
||||
('ST_Distance_Sphere', None,
|
||||
'''Returns minimum distance in meters between two lon/lat geometries. Uses a spherical earth and radius of 6370986 meters. Faster than ``ST_Distance_Spheroid``, but less accurate. PostGIS versions prior to 1.5 only implemented for points.'''),
|
||||
]
|
||||
# fmt: on
|
||||
@@ -0,0 +1,87 @@
|
||||
from textwrap import TextWrapper
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
|
||||
def _wrap_docstring(docstring: str) -> str:
|
||||
wrapper = TextWrapper(width=100)
|
||||
lines = []
|
||||
for long_line in docstring.splitlines(keepends=False):
|
||||
lines.extend(wrapper.wrap(long_line))
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _get_docstring(name: str, doc: Union[None, str, Tuple[str, str]], type_: Optional[type]) -> str:
|
||||
doc_string_parts = []
|
||||
|
||||
if isinstance(doc, tuple):
|
||||
doc_string_parts.append(_wrap_docstring(doc[0]))
|
||||
doc_string_parts.append("see https://postgis.net/docs/{0}.html".format(doc[1]))
|
||||
elif doc is not None:
|
||||
doc_string_parts.append(_wrap_docstring(doc))
|
||||
doc_string_parts.append("see https://postgis.net/docs/{0}.html".format(name))
|
||||
|
||||
if type_ is not None:
|
||||
return_type_str = "{0}.{1}".format(type_.__module__, type_.__name__)
|
||||
doc_string_parts.append("Return type: :class:`{0}`.".format(return_type_str))
|
||||
|
||||
return "\n\n".join(doc_string_parts)
|
||||
|
||||
|
||||
def _replace_indent(text: str, indent: str) -> str:
|
||||
lines = []
|
||||
for i, line in enumerate(text.splitlines()):
|
||||
if i == 0 or not line.strip():
|
||||
lines.append(line)
|
||||
else:
|
||||
lines.append(f"{indent}{line}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _generate_stubs() -> str:
|
||||
"""Generates type stubs for the dynamic functions described in `geoalchemy2/_functions.py`."""
|
||||
from geoalchemy2._functions import _FUNCTIONS
|
||||
from geoalchemy2.functions import ST_AsGeoJSON
|
||||
|
||||
header = '''\
|
||||
# this file is automatically generated
|
||||
from typing import List
|
||||
|
||||
from sqlalchemy.sql import functions
|
||||
from sqlalchemy.sql.elements import ColumnElement
|
||||
|
||||
import geoalchemy2.types
|
||||
|
||||
class GenericFunction(functions.GenericFunction): ...
|
||||
|
||||
class TableRowElement(ColumnElement):
|
||||
inherit_cache: bool = ...
|
||||
"""The cache is disabled for this class."""
|
||||
|
||||
def __init__(self, selectable: bool) -> None: ...
|
||||
@property # type: ignore[override]
|
||||
def _from_objects(self) -> List[bool]: ...
|
||||
'''
|
||||
stub_file_parts = [header]
|
||||
|
||||
functions = _FUNCTIONS.copy()
|
||||
functions.insert(0, ("ST_AsGeoJSON", None, ST_AsGeoJSON.__doc__))
|
||||
|
||||
for name, type_, doc_parts in functions:
|
||||
doc = _replace_indent(_get_docstring(name, doc_parts, type_), " ")
|
||||
|
||||
if type_ is None:
|
||||
type_str = ""
|
||||
else:
|
||||
type_str = f"\n\n type = {type_.__module__}.{type_.__name__}()"
|
||||
|
||||
signature = f'''\
|
||||
class {name}(GenericFunction):
|
||||
"""
|
||||
{doc}
|
||||
"""{type_str}
|
||||
'''
|
||||
stub_file_parts.append(signature)
|
||||
|
||||
return "\n".join(stub_file_parts)
|
||||
115
.venv/lib/python3.12/site-packages/geoalchemy2/admin/__init__.py
Normal file
115
.venv/lib/python3.12/site-packages/geoalchemy2/admin/__init__.py
Normal file
@@ -0,0 +1,115 @@
|
||||
"""This module defines the functions used for administration tasks."""
|
||||
|
||||
from sqlalchemy import Column
|
||||
from sqlalchemy import Index
|
||||
from sqlalchemy import Table
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from geoalchemy2.admin import dialects
|
||||
from geoalchemy2.admin.dialects.common import _check_spatial_type
|
||||
from geoalchemy2.admin.dialects.common import _spatial_idx_name
|
||||
from geoalchemy2.exc import ArgumentError
|
||||
from geoalchemy2.types import Geography
|
||||
from geoalchemy2.types import Geometry
|
||||
from geoalchemy2.types import Raster
|
||||
|
||||
|
||||
def select_dialect(dialect_name):
|
||||
"""Select the dialect from its name."""
|
||||
known_dialects = {
|
||||
"geopackage": dialects.geopackage,
|
||||
"mysql": dialects.mysql,
|
||||
"mariadb": dialects.mysql,
|
||||
"postgresql": dialects.postgresql,
|
||||
"sqlite": dialects.sqlite,
|
||||
}
|
||||
return known_dialects.get(dialect_name, dialects.common)
|
||||
|
||||
|
||||
def setup_ddl_event_listeners():
|
||||
"""Setup the DDL event listeners to automatically process spatial columns."""
|
||||
|
||||
@event.listens_for(Table, "before_create")
|
||||
def before_create(table, bind, **kw):
|
||||
"""Handle spatial indexes."""
|
||||
select_dialect(bind.dialect.name).before_create(table, bind, **kw)
|
||||
|
||||
@event.listens_for(Table, "after_create")
|
||||
def after_create(table, bind, **kw):
|
||||
"""Restore original column list including managed Geometry columns."""
|
||||
select_dialect(bind.dialect.name).after_create(table, bind, **kw)
|
||||
|
||||
@event.listens_for(Table, "before_drop")
|
||||
def before_drop(table, bind, **kw):
|
||||
"""Drop the managed Geometry columns."""
|
||||
select_dialect(bind.dialect.name).before_drop(table, bind, **kw)
|
||||
|
||||
@event.listens_for(Table, "after_drop")
|
||||
def after_drop(table, bind, **kw):
|
||||
"""Restore original column list including managed Geometry columns."""
|
||||
select_dialect(bind.dialect.name).after_drop(table, bind, **kw)
|
||||
|
||||
@event.listens_for(Column, "after_parent_attach")
|
||||
def after_parent_attach(column, table):
|
||||
"""Automatically add spatial indexes."""
|
||||
if not isinstance(table, Table):
|
||||
# For old versions of SQLAlchemy, subqueries might trigger the after_parent_attach event
|
||||
# with a selectable as table, so we want to skip this case.
|
||||
return
|
||||
|
||||
if not getattr(column.type, "nullable", True):
|
||||
column.nullable = column.type.nullable
|
||||
elif hasattr(column.type, "nullable"):
|
||||
column.type.nullable = column.nullable
|
||||
|
||||
if not getattr(column.type, "spatial_index", False) and getattr(
|
||||
column.type, "use_N_D_index", False
|
||||
):
|
||||
raise ArgumentError("Arg Error(use_N_D_index): spatial_index must be True")
|
||||
|
||||
if not getattr(column.type, "spatial_index", False):
|
||||
# If the column is managed, the indexes are created after the table
|
||||
return
|
||||
|
||||
try:
|
||||
if column.type._spatial_index_reflected:
|
||||
return
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
kwargs = {
|
||||
"postgresql_using": "gist",
|
||||
"_column_flag": True,
|
||||
}
|
||||
col = column
|
||||
if _check_spatial_type(column.type, (Geometry, Geography)):
|
||||
if column.type.use_N_D_index:
|
||||
kwargs["postgresql_ops"] = {column.name: "gist_geometry_ops_nd"}
|
||||
elif _check_spatial_type(column.type, Raster):
|
||||
col = func.ST_ConvexHull(column)
|
||||
|
||||
table.append_constraint(
|
||||
Index(
|
||||
_spatial_idx_name(table.name, column.name),
|
||||
col,
|
||||
**kwargs,
|
||||
)
|
||||
)
|
||||
|
||||
@event.listens_for(Table, "column_reflect")
|
||||
def _reflect_geometry_column(inspector, table, column_info):
|
||||
select_dialect(inspector.bind.dialect.name).reflect_geometry_column(
|
||||
inspector, table, column_info
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"dialects",
|
||||
"select_dialect",
|
||||
"setup_ddl_event_listeners",
|
||||
]
|
||||
|
||||
|
||||
def __dir__():
|
||||
return __all__
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
"""This module defines some dialect-specific functions used for administration tasks."""
|
||||
|
||||
from geoalchemy2.admin.dialects import common # noqa
|
||||
from geoalchemy2.admin.dialects import geopackage # noqa
|
||||
from geoalchemy2.admin.dialects import mysql # noqa
|
||||
from geoalchemy2.admin.dialects import postgresql # noqa
|
||||
from geoalchemy2.admin.dialects import sqlite # noqa
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,104 @@
|
||||
"""This module defines functions used by several dialects."""
|
||||
|
||||
import sqlalchemy
|
||||
from packaging import version
|
||||
from sqlalchemy import Column
|
||||
from sqlalchemy.sql import expression
|
||||
from sqlalchemy.types import TypeDecorator
|
||||
|
||||
from geoalchemy2.types import Geometry
|
||||
|
||||
_SQLALCHEMY_VERSION_BEFORE_14 = version.parse(sqlalchemy.__version__) < version.parse("1.4")
|
||||
|
||||
|
||||
def _spatial_idx_name(table_name, column_name):
|
||||
return "idx_{}_{}".format(table_name, column_name)
|
||||
|
||||
|
||||
def _format_select_args(*args):
|
||||
if _SQLALCHEMY_VERSION_BEFORE_14:
|
||||
return [args]
|
||||
else:
|
||||
return args
|
||||
|
||||
|
||||
def check_management(*args):
|
||||
"""Default function to check management (always True by default)."""
|
||||
return True
|
||||
|
||||
|
||||
def _get_gis_cols(table, spatial_types, dialect, check_col_management=None):
|
||||
if check_col_management is not None:
|
||||
func = check_col_management
|
||||
else:
|
||||
func = check_management
|
||||
return [
|
||||
col
|
||||
for col in table.columns
|
||||
if (
|
||||
isinstance(col, Column)
|
||||
and _check_spatial_type(col.type, spatial_types, dialect)
|
||||
and func(col)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def _check_spatial_type(tested_type, spatial_types, dialect=None):
|
||||
return isinstance(tested_type, spatial_types) or (
|
||||
isinstance(tested_type, TypeDecorator)
|
||||
and isinstance(tested_type.load_dialect_impl(dialect), spatial_types)
|
||||
)
|
||||
|
||||
|
||||
def _get_dispatch_info(table, bind, check_col_management=None):
|
||||
"""Get info required for dispatch events."""
|
||||
dialect = bind.dialect
|
||||
|
||||
# Filter Geometry columns from the table
|
||||
# Note: Geography and PostGIS >= 2.0 don't need this
|
||||
gis_cols = _get_gis_cols(table, Geometry, dialect, check_col_management=check_col_management)
|
||||
|
||||
# Find all other columns that are not managed Geometries
|
||||
regular_cols = [x for x in table.columns if x not in gis_cols]
|
||||
|
||||
return dialect, gis_cols, regular_cols
|
||||
|
||||
|
||||
def _update_table_for_dispatch(table, regular_cols):
|
||||
"""Update the table before dispatch events."""
|
||||
# Save original table column list for later
|
||||
table.info["_saved_columns"] = table.columns
|
||||
|
||||
# Temporarily patch a set of columns not including the
|
||||
# managed Geometry columns
|
||||
column_collection = expression.ColumnCollection()
|
||||
for col in regular_cols:
|
||||
column_collection.add(col)
|
||||
table.columns = column_collection
|
||||
|
||||
|
||||
def setup_create_drop(table, bind, check_col_management=None):
|
||||
"""Prepare the table for before_create and before_drop events."""
|
||||
dialect, gis_cols, regular_cols = _get_dispatch_info(table, bind, check_col_management)
|
||||
_update_table_for_dispatch(table, regular_cols)
|
||||
return dialect, gis_cols, regular_cols
|
||||
|
||||
|
||||
def reflect_geometry_column(inspector, table, column_info):
|
||||
return
|
||||
|
||||
|
||||
def before_create(table, bind, **kw):
|
||||
return
|
||||
|
||||
|
||||
def after_create(table, bind, **kw):
|
||||
return
|
||||
|
||||
|
||||
def before_drop(table, bind, **kw):
|
||||
return
|
||||
|
||||
|
||||
def after_drop(table, bind, **kw):
|
||||
return
|
||||
@@ -0,0 +1,381 @@
|
||||
"""This module defines specific functions for GeoPackage dialect.
|
||||
|
||||
See GeoPackage specifications here: http://www.geopackage.org/spec/
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.dialects import registry
|
||||
from sqlalchemy.dialects.sqlite.pysqlite import SQLiteDialect_pysqlite
|
||||
from sqlalchemy.ext.compiler import compiles
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.sql import select
|
||||
|
||||
from geoalchemy2 import functions
|
||||
from geoalchemy2.admin.dialects.common import _check_spatial_type
|
||||
from geoalchemy2.admin.dialects.common import _format_select_args
|
||||
from geoalchemy2.admin.dialects.common import _spatial_idx_name
|
||||
from geoalchemy2.admin.dialects.common import setup_create_drop
|
||||
from geoalchemy2.admin.dialects.sqlite import _SQLITE_FUNCTIONS
|
||||
from geoalchemy2.admin.dialects.sqlite import get_col_dim
|
||||
from geoalchemy2.admin.dialects.sqlite import load_spatialite_driver
|
||||
from geoalchemy2.types import Geography
|
||||
from geoalchemy2.types import Geometry
|
||||
from geoalchemy2.types import _DummyGeometry
|
||||
|
||||
|
||||
class GeoPackageDialect(SQLiteDialect_pysqlite):
|
||||
"""Define a specific dialect for GeoPackage."""
|
||||
|
||||
name = "geopackage"
|
||||
driver = "gpkg"
|
||||
|
||||
supports_statement_cache = True
|
||||
"""Enable caching for GeoPackage dialect."""
|
||||
|
||||
|
||||
registry.register("gpkg", "geoalchemy2.admin.dialects.geopackage", "GeoPackageDialect")
|
||||
|
||||
|
||||
def load_geopackage_driver(dbapi_conn, *args):
|
||||
"""Load SpatiaLite extension in GeoPackage connection and set VirtualGpkg and Amphibious modes.
|
||||
|
||||
.. Warning::
|
||||
The path to the SpatiaLite module should be set in the `SPATIALITE_LIBRARY_PATH`
|
||||
environment variable.
|
||||
|
||||
Args:
|
||||
dbapi_conn: The DBAPI connection.
|
||||
"""
|
||||
load_spatialite_driver(dbapi_conn, *args)
|
||||
|
||||
dbapi_conn.execute("SELECT AutoGpkgStart();")
|
||||
dbapi_conn.execute("SELECT EnableGpkgAmphibiousMode();")
|
||||
|
||||
|
||||
def init_geopackage(dbapi_conn, *args):
|
||||
"""Initialize GeoPackage tables.
|
||||
|
||||
Args:
|
||||
dbapi_conn: The DBAPI connection.
|
||||
|
||||
.. Warning::
|
||||
No EPSG SRID is loaded in the `gpkg_spatial_ref_sys` table after initialization but
|
||||
it is possible to load other EPSG SRIDs afterwards using the
|
||||
`gpkgInsertEpsgSRID(srid)`.
|
||||
Nevertheless, SRIDs of newly created tables are automatically added.
|
||||
"""
|
||||
if not dbapi_conn.execute("SELECT CheckGeoPackageMetaData();").fetchone()[0]:
|
||||
# This only works on the main database
|
||||
dbapi_conn.execute("SELECT gpkgCreateBaseTables();")
|
||||
|
||||
|
||||
def load_spatialite_gpkg(*args, **kwargs):
|
||||
"""Load SpatiaLite extension in GeoPackage and initialize internal tables.
|
||||
|
||||
See :func:`geoalchemy2.admin.dialects.geopackage.load_geopackage_driver` and
|
||||
:func:`geoalchemy2.admin.dialects.geopackage.init_geopackage` functions for details about
|
||||
arguments.
|
||||
"""
|
||||
load_geopackage_driver(*args)
|
||||
init_geopackage(*args, **kwargs)
|
||||
|
||||
|
||||
def _get_spatialite_attrs(bind, table_name, col_name):
|
||||
attrs = bind.execute(
|
||||
text(
|
||||
"""SELECT
|
||||
A.geometry_type_name,
|
||||
A.srs_id,
|
||||
A.z,
|
||||
A.m,
|
||||
IFNULL(B.has_index, 0) AS has_index
|
||||
FROM gpkg_geometry_columns
|
||||
AS A
|
||||
LEFT JOIN (
|
||||
SELECT table_name, column_name, COUNT(*) AS has_index
|
||||
FROM gpkg_extensions
|
||||
WHERE LOWER(table_name) = LOWER(:table_name)
|
||||
AND column_name = :column_name
|
||||
AND extension_name = 'gpkg_rtree_index'
|
||||
) AS B
|
||||
ON LOWER(A.table_name) = LOWER(B.table_name)
|
||||
AND A.column_name = B.column_name
|
||||
WHERE LOWER(A.table_name) = LOWER(:table_name)
|
||||
AND A.column_name = :column_name;
|
||||
"""
|
||||
).bindparams(table_name=table_name, column_name=col_name)
|
||||
).fetchone()
|
||||
if attrs is None:
|
||||
# If the column is not registered as a spatial column we ignore it
|
||||
return None
|
||||
geometry_type, srid, has_z, has_m, has_index = attrs
|
||||
coord_dimension = "XY"
|
||||
if has_z:
|
||||
coord_dimension += "Z"
|
||||
if has_m:
|
||||
coord_dimension += "M"
|
||||
col_attributes = geometry_type, coord_dimension, srid, has_index
|
||||
return col_attributes
|
||||
|
||||
|
||||
def _setup_dummy_type(table, gis_cols):
|
||||
"""Setup dummy type for new Geometry columns so they can be updated later."""
|
||||
for col in gis_cols:
|
||||
# Add dummy columns with GEOMETRY type
|
||||
type_str = re.fullmatch("(.+?)[ZMzm]*", col.type.geometry_type).group(1)
|
||||
col._actual_type = col.type
|
||||
col.type = _DummyGeometry(geometry_type=type_str)
|
||||
table.columns = table.info["_saved_columns"]
|
||||
|
||||
|
||||
def create_spatial_index(bind, table, col):
|
||||
"""Create spatial index on the given column."""
|
||||
stmt = select(*_format_select_args(func.gpkgAddSpatialIndex(table.name, col.name)))
|
||||
stmt = stmt.execution_options(autocommit=True)
|
||||
bind.execute(stmt)
|
||||
|
||||
|
||||
def disable_spatial_index(bind, table, col):
|
||||
"""Disable spatial indexes if present."""
|
||||
for i in ["", "_node", "_parent", "_rowid"]:
|
||||
bind.execute(
|
||||
text(
|
||||
"DROP TABLE IF EXISTS rtree_{}_{}{};".format(
|
||||
table.name,
|
||||
col.name,
|
||||
i,
|
||||
)
|
||||
)
|
||||
)
|
||||
bind.execute(
|
||||
text(
|
||||
"""DELETE FROM gpkg_extensions
|
||||
WHERE LOWER(table_name) = LOWER(:table_name)
|
||||
AND column_name = :column_name
|
||||
AND extension_name = 'gpkg_rtree_index';"""
|
||||
).bindparams(
|
||||
table_name=table.name,
|
||||
column_name=col.name,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def reflect_geometry_column(inspector, table, column_info):
|
||||
"""Reflect a column of type Geometry with GeoPackage dialect."""
|
||||
# Get geometry type, SRID and spatial index from the SpatiaLite metadata
|
||||
if not isinstance(column_info.get("type"), Geometry):
|
||||
return
|
||||
col_attributes = _get_spatialite_attrs(inspector.bind, table.name, column_info["name"])
|
||||
if col_attributes is not None:
|
||||
geometry_type, coord_dimension, srid, spatial_index = col_attributes
|
||||
|
||||
coord_dimension = {
|
||||
"XY": 2,
|
||||
"XYZ": 3,
|
||||
"XYM": 3,
|
||||
"XYZM": 4,
|
||||
}.get(coord_dimension, coord_dimension)
|
||||
|
||||
# Set attributes
|
||||
column_info["type"].geometry_type = geometry_type
|
||||
column_info["type"].dimension = coord_dimension
|
||||
column_info["type"].srid = srid
|
||||
column_info["type"].spatial_index = bool(spatial_index)
|
||||
|
||||
# Spatial indexes are not automatically reflected with GeoPackage dialect
|
||||
column_info["type"]._spatial_index_reflected = False
|
||||
|
||||
|
||||
def before_create(table, bind, **kw):
|
||||
"""Handle spatial indexes during the before_create event."""
|
||||
dialect, gis_cols, regular_cols = setup_create_drop(table, bind)
|
||||
|
||||
# Remove the spatial indexes from the table metadata because they should not be
|
||||
# created during the table.create() step since the associated columns do not exist
|
||||
# at this time.
|
||||
table.info["_after_create_indexes"] = []
|
||||
current_indexes = set(table.indexes)
|
||||
for idx in current_indexes:
|
||||
for col in table.info["_saved_columns"]:
|
||||
if _check_spatial_type(col.type, Geometry, dialect) and col in idx.columns.values():
|
||||
table.indexes.remove(idx)
|
||||
if idx.name != _spatial_idx_name(table.name, col.name) or not getattr(
|
||||
col.type, "spatial_index", False
|
||||
):
|
||||
table.info["_after_create_indexes"].append(idx)
|
||||
|
||||
if len(gis_cols) > 1:
|
||||
raise ValueError("Only one geometry column is allowed for a table stored in a GeoPackage.")
|
||||
elif len(gis_cols) == 1:
|
||||
col = gis_cols[0]
|
||||
srid = col.type.srid
|
||||
|
||||
if col.type.geometry_type is None:
|
||||
col.type.geometry_type = "GEOMETRY"
|
||||
|
||||
# Add the SRID of the table in 'gpkg_spatial_ref_sys' if this table exists
|
||||
if not bind.execute(
|
||||
text("SELECT COUNT(*) FROM gpkg_spatial_ref_sys WHERE srs_id = :srid;").bindparams(
|
||||
srid=srid
|
||||
)
|
||||
).scalar():
|
||||
bind.execute(text("SELECT gpkgInsertEpsgSRID(:srid)").bindparams(srid=srid))
|
||||
_setup_dummy_type(table, gis_cols)
|
||||
|
||||
|
||||
def after_create(table, bind, **kw):
|
||||
"""Handle spatial indexes during the after_create event."""
|
||||
dialect = bind.dialect
|
||||
|
||||
for col in table.columns:
|
||||
# Add the managed Geometry columns with gpkgAddGeometryColumn()
|
||||
if _check_spatial_type(col.type, Geometry, dialect):
|
||||
col.type = col._actual_type
|
||||
del col._actual_type
|
||||
dimension = get_col_dim(col)
|
||||
has_z = "Z" in dimension
|
||||
has_m = "M" in dimension
|
||||
|
||||
bind.execute(
|
||||
text(
|
||||
"""INSERT INTO gpkg_contents
|
||||
VALUES (
|
||||
:table_name,
|
||||
'features',
|
||||
:table_name,
|
||||
"",
|
||||
strftime('%Y-%m-%dT%H:%M:%fZ', CURRENT_TIMESTAMP),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
:srid
|
||||
);"""
|
||||
).bindparams(
|
||||
table_name=table.name,
|
||||
srid=col.type.srid,
|
||||
)
|
||||
)
|
||||
bind.execute(
|
||||
text(
|
||||
"""INSERT INTO gpkg_geometry_columns
|
||||
VALUES (:table_name, :column_name, :geometry_type, :srid, :has_z, :has_m);"""
|
||||
).bindparams(
|
||||
table_name=table.name,
|
||||
column_name=col.name,
|
||||
geometry_type=col.type.geometry_type,
|
||||
srid=col.type.srid,
|
||||
has_z=has_z,
|
||||
has_m=has_m,
|
||||
)
|
||||
)
|
||||
stmt = select(*_format_select_args(func.gpkgAddGeometryTriggers(table.name, col.name)))
|
||||
stmt = stmt.execution_options(autocommit=True)
|
||||
bind.execute(stmt)
|
||||
|
||||
for col in table.columns:
|
||||
# Add spatial indexes for the Geometry and Geography columns
|
||||
# TODO: Check that the Geography type makes sense here
|
||||
if (
|
||||
_check_spatial_type(col.type, (Geometry, Geography), dialect)
|
||||
and col.type.spatial_index is True
|
||||
):
|
||||
create_spatial_index(bind, table, col)
|
||||
|
||||
for idx in table.info.pop("_after_create_indexes"):
|
||||
table.indexes.add(idx)
|
||||
idx.create(bind=bind)
|
||||
|
||||
|
||||
def before_drop(table, bind, **kw):
|
||||
"""Handle spatial indexes during the before_drop event."""
|
||||
dialect, gis_cols, regular_cols = setup_create_drop(table, bind)
|
||||
|
||||
for col in gis_cols:
|
||||
# Disable spatial indexes if present
|
||||
# TODO: This is useless but if we remove it then the disable_spatial_index should be
|
||||
# tested separately
|
||||
disable_spatial_index(bind, table, col)
|
||||
|
||||
# Remove metadata from internal tables
|
||||
# (this is equivalent to DiscardGeometryColumn but for GeoPackage)
|
||||
bind.execute(
|
||||
text(
|
||||
"""DELETE FROM gpkg_extensions
|
||||
WHERE LOWER(table_name) = LOWER(:table_name)
|
||||
AND column_name = :column_name;"""
|
||||
).bindparams(
|
||||
table_name=table.name,
|
||||
column_name=col.name,
|
||||
)
|
||||
)
|
||||
bind.execute(
|
||||
text(
|
||||
"""DELETE FROM gpkg_geometry_columns
|
||||
WHERE LOWER(table_name) = LOWER(:table_name)
|
||||
AND column_name = :column_name;"""
|
||||
).bindparams(
|
||||
table_name=table.name,
|
||||
column_name=col.name,
|
||||
)
|
||||
)
|
||||
bind.execute(
|
||||
text(
|
||||
"""DELETE FROM gpkg_contents
|
||||
WHERE LOWER(table_name) = LOWER(:table_name);"""
|
||||
).bindparams(table_name=table.name)
|
||||
)
|
||||
|
||||
|
||||
def after_drop(table, bind, **kw):
|
||||
"""Handle spatial indexes during the after_drop event."""
|
||||
table.columns = table.info.pop("_saved_columns")
|
||||
|
||||
|
||||
def _compiles_gpkg(cls, fn):
|
||||
def _compile_gpkg(element, compiler, **kw):
|
||||
return "{}({})".format(fn, compiler.process(element.clauses, **kw))
|
||||
|
||||
compiles(getattr(functions, cls), "geopackage")(_compile_gpkg)
|
||||
|
||||
|
||||
def register_gpkg_mapping(mapping):
|
||||
"""Register compilation mappings for the given functions.
|
||||
|
||||
Args:
|
||||
mapping: Should have the following form::
|
||||
|
||||
{
|
||||
"function_name_1": "gpkg_function_name_1",
|
||||
"function_name_2": "gpkg_function_name_2",
|
||||
...
|
||||
}
|
||||
"""
|
||||
for cls, fn in mapping.items():
|
||||
_compiles_gpkg(cls, fn)
|
||||
|
||||
|
||||
register_gpkg_mapping(_SQLITE_FUNCTIONS)
|
||||
|
||||
|
||||
def create_spatial_ref_sys_view(bind):
|
||||
"""Create the `spatial_ref_sys` view from the `gpkg_spatial_ref_sys` table.
|
||||
|
||||
.. Note::
|
||||
|
||||
This is usually only needed to use the `ST_Transform` function on GeoPackage data
|
||||
because this function, when used with SpatiaLite, requires the `spatial_ref_sys` table.
|
||||
"""
|
||||
bind.execute(
|
||||
text(
|
||||
"""CREATE VIEW spatial_ref_sys AS
|
||||
SELECT
|
||||
srs_id AS srid,
|
||||
organization AS auth_name,
|
||||
organization_coordsys_id AS auth_srid,
|
||||
definition AS srtext
|
||||
FROM gpkg_spatial_ref_sys;"""
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,200 @@
|
||||
"""This module defines specific functions for MySQL dialect."""
|
||||
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.compiler import compiles
|
||||
from sqlalchemy.sql.sqltypes import NullType
|
||||
|
||||
from geoalchemy2 import functions
|
||||
from geoalchemy2.admin.dialects.common import _check_spatial_type
|
||||
from geoalchemy2.admin.dialects.common import _spatial_idx_name
|
||||
from geoalchemy2.admin.dialects.common import setup_create_drop
|
||||
from geoalchemy2.types import Geography
|
||||
from geoalchemy2.types import Geometry
|
||||
|
||||
_POSSIBLE_TYPES = [
|
||||
"geometry",
|
||||
"point",
|
||||
"linestring",
|
||||
"polygon",
|
||||
"multipoint",
|
||||
"multilinestring",
|
||||
"multipolygon",
|
||||
"geometrycollection",
|
||||
]
|
||||
|
||||
|
||||
def reflect_geometry_column(inspector, table, column_info):
|
||||
"""Reflect a column of type Geometry with Postgresql dialect."""
|
||||
if not isinstance(column_info.get("type"), (Geometry, NullType)):
|
||||
return
|
||||
|
||||
column_name = column_info.get("name")
|
||||
schema = table.schema or inspector.default_schema_name
|
||||
|
||||
# Check geometry type, SRID and if the column is nullable
|
||||
geometry_type_query = """SELECT DATA_TYPE, SRS_ID, IS_NULLABLE
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_NAME = '{}' and COLUMN_NAME = '{}'""".format(
|
||||
table.name, column_name
|
||||
)
|
||||
if schema is not None:
|
||||
geometry_type_query += """ and table_schema = '{}'""".format(schema)
|
||||
geometry_type, srid, nullable_str = inspector.bind.execute(text(geometry_type_query)).one()
|
||||
is_nullable = str(nullable_str).lower() == "yes"
|
||||
|
||||
if geometry_type not in _POSSIBLE_TYPES:
|
||||
return
|
||||
|
||||
# Check if the column has spatial index
|
||||
has_index_query = """SELECT DISTINCT
|
||||
INDEX_TYPE
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_NAME = '{}' and COLUMN_NAME = '{}'""".format(
|
||||
table.name, column_name
|
||||
)
|
||||
if schema is not None:
|
||||
has_index_query += """ and TABLE_SCHEMA = '{}'""".format(schema)
|
||||
spatial_index_res = inspector.bind.execute(text(has_index_query)).scalar()
|
||||
spatial_index = str(spatial_index_res).lower() == "spatial"
|
||||
|
||||
# Set attributes
|
||||
column_info["type"] = Geometry(
|
||||
geometry_type=geometry_type.upper(),
|
||||
srid=srid,
|
||||
spatial_index=spatial_index,
|
||||
nullable=is_nullable,
|
||||
_spatial_index_reflected=True,
|
||||
)
|
||||
|
||||
|
||||
def before_create(table, bind, **kw):
|
||||
"""Handle spatial indexes during the before_create event."""
|
||||
dialect, gis_cols, regular_cols = setup_create_drop(table, bind)
|
||||
|
||||
# Remove the spatial indexes from the table metadata because they should not be
|
||||
# created during the table.create() step since the associated columns do not exist
|
||||
# at this time.
|
||||
table.info["_after_create_indexes"] = []
|
||||
current_indexes = set(table.indexes)
|
||||
for idx in current_indexes:
|
||||
for col in table.info["_saved_columns"]:
|
||||
if (_check_spatial_type(col.type, Geometry, dialect)) and col in idx.columns.values():
|
||||
table.indexes.remove(idx)
|
||||
if idx.name != _spatial_idx_name(table.name, col.name) or not getattr(
|
||||
col.type, "spatial_index", False
|
||||
):
|
||||
table.info["_after_create_indexes"].append(idx)
|
||||
|
||||
table.columns = table.info.pop("_saved_columns")
|
||||
|
||||
|
||||
def after_create(table, bind, **kw):
|
||||
"""Handle spatial indexes during the after_create event."""
|
||||
# Restore original column list including managed Geometry columns
|
||||
dialect = bind.dialect
|
||||
|
||||
# table.columns = table.info.pop("_saved_columns")
|
||||
|
||||
for col in table.columns:
|
||||
# Add spatial indices for the Geometry and Geography columns
|
||||
if (
|
||||
_check_spatial_type(col.type, (Geometry, Geography), dialect)
|
||||
and col.type.spatial_index is True
|
||||
):
|
||||
# If the index does not exist, define it and create it
|
||||
if not [i for i in table.indexes if col in i.columns.values()]:
|
||||
sql = "ALTER TABLE {} ADD SPATIAL INDEX({});".format(table.name, col.name)
|
||||
q = text(sql)
|
||||
bind.execute(q)
|
||||
|
||||
for idx in table.info.pop("_after_create_indexes"):
|
||||
table.indexes.add(idx)
|
||||
|
||||
|
||||
def before_drop(table, bind, **kw):
|
||||
return
|
||||
|
||||
|
||||
def after_drop(table, bind, **kw):
|
||||
return
|
||||
|
||||
|
||||
_MYSQL_FUNCTIONS = {
|
||||
"ST_AsEWKB": "ST_AsBinary",
|
||||
}
|
||||
|
||||
|
||||
def _compiles_mysql(cls, fn):
|
||||
def _compile_mysql(element, compiler, **kw):
|
||||
return "{}({})".format(fn, compiler.process(element.clauses, **kw))
|
||||
|
||||
compiles(getattr(functions, cls), "mysql")(_compile_mysql)
|
||||
compiles(getattr(functions, cls), "mariadb")(_compile_mysql)
|
||||
|
||||
|
||||
def register_mysql_mapping(mapping):
|
||||
"""Register compilation mappings for the given functions.
|
||||
|
||||
Args:
|
||||
mapping: Should have the following form::
|
||||
|
||||
{
|
||||
"function_name_1": "mysql_function_name_1",
|
||||
"function_name_2": "mysql_function_name_2",
|
||||
...
|
||||
}
|
||||
"""
|
||||
for cls, fn in mapping.items():
|
||||
_compiles_mysql(cls, fn)
|
||||
|
||||
|
||||
register_mysql_mapping(_MYSQL_FUNCTIONS)
|
||||
|
||||
|
||||
def _compile_GeomFromText_MySql(element, compiler, **kw):
|
||||
element.identifier = "ST_GeomFromText"
|
||||
compiled = compiler.process(element.clauses, **kw)
|
||||
srid = element.type.srid
|
||||
|
||||
if srid > 0:
|
||||
return "{}({}, {})".format(element.identifier, compiled, srid)
|
||||
else:
|
||||
return "{}({})".format(element.identifier, compiled)
|
||||
|
||||
|
||||
def _compile_GeomFromWKB_MySql(element, compiler, **kw):
|
||||
element.identifier = "ST_GeomFromWKB"
|
||||
wkb_data = list(element.clauses)[0].value
|
||||
if isinstance(wkb_data, memoryview):
|
||||
list(element.clauses)[0].value = wkb_data.tobytes()
|
||||
compiled = compiler.process(element.clauses, **kw)
|
||||
srid = element.type.srid
|
||||
|
||||
if srid > 0:
|
||||
return "{}({}, {})".format(element.identifier, compiled, srid)
|
||||
else:
|
||||
return "{}({})".format(element.identifier, compiled)
|
||||
|
||||
|
||||
@compiles(functions.ST_GeomFromText, "mysql") # type: ignore
|
||||
@compiles(functions.ST_GeomFromText, "mariadb") # type: ignore
|
||||
def _MySQL_ST_GeomFromText(element, compiler, **kw):
|
||||
return _compile_GeomFromText_MySql(element, compiler, **kw)
|
||||
|
||||
|
||||
@compiles(functions.ST_GeomFromEWKT, "mysql") # type: ignore
|
||||
@compiles(functions.ST_GeomFromEWKT, "mariadb") # type: ignore
|
||||
def _MySQL_ST_GeomFromEWKT(element, compiler, **kw):
|
||||
return _compile_GeomFromText_MySql(element, compiler, **kw)
|
||||
|
||||
|
||||
@compiles(functions.ST_GeomFromWKB, "mysql") # type: ignore
|
||||
@compiles(functions.ST_GeomFromWKB, "mariadb") # type: ignore
|
||||
def _MySQL_ST_GeomFromWKB(element, compiler, **kw):
|
||||
return _compile_GeomFromWKB_MySql(element, compiler, **kw)
|
||||
|
||||
|
||||
@compiles(functions.ST_GeomFromEWKB, "mysql") # type: ignore
|
||||
@compiles(functions.ST_GeomFromEWKB, "mariadb") # type: ignore
|
||||
def _MySQL_ST_GeomFromEWKB(element, compiler, **kw):
|
||||
return _compile_GeomFromWKB_MySql(element, compiler, **kw)
|
||||
@@ -0,0 +1,162 @@
|
||||
"""This module defines specific functions for Postgresql dialect."""
|
||||
|
||||
from sqlalchemy import Index
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.sql import select
|
||||
|
||||
from geoalchemy2.admin.dialects.common import _check_spatial_type
|
||||
from geoalchemy2.admin.dialects.common import _format_select_args
|
||||
from geoalchemy2.admin.dialects.common import _spatial_idx_name
|
||||
from geoalchemy2.admin.dialects.common import setup_create_drop
|
||||
from geoalchemy2.types import Geography
|
||||
from geoalchemy2.types import Geometry
|
||||
|
||||
|
||||
def check_management(column):
|
||||
"""Check if the column should be managed."""
|
||||
return getattr(column.type, "use_typmod", None) is False
|
||||
|
||||
|
||||
def create_spatial_index(bind, table, col):
|
||||
"""Create spatial index on the given column."""
|
||||
if col.type.use_N_D_index:
|
||||
postgresql_ops = {col.name: "gist_geometry_ops_nd"}
|
||||
else:
|
||||
postgresql_ops = {}
|
||||
idx = Index(
|
||||
_spatial_idx_name(table.name, col.name),
|
||||
col,
|
||||
postgresql_using="gist",
|
||||
postgresql_ops=postgresql_ops,
|
||||
_column_flag=True,
|
||||
)
|
||||
idx.create(bind=bind)
|
||||
|
||||
|
||||
def reflect_geometry_column(inspector, table, column_info):
|
||||
"""Reflect a column of type Geometry with Postgresql dialect."""
|
||||
if not isinstance(column_info.get("type"), Geometry):
|
||||
return
|
||||
geo_type = column_info["type"]
|
||||
geometry_type = geo_type.geometry_type
|
||||
coord_dimension = geo_type.dimension
|
||||
if geometry_type.endswith("ZM"):
|
||||
coord_dimension = 4
|
||||
elif geometry_type[-1] in ["Z", "M"]:
|
||||
coord_dimension = 3
|
||||
|
||||
# Query to check a given column has spatial index
|
||||
if table.schema is not None:
|
||||
schema_part = " AND nspname = '{}'".format(table.schema)
|
||||
else:
|
||||
schema_part = ""
|
||||
|
||||
has_index_query = """SELECT (indexrelid IS NOT NULL) AS has_index
|
||||
FROM (
|
||||
SELECT
|
||||
n.nspname,
|
||||
c.relname,
|
||||
c.oid AS relid,
|
||||
a.attname,
|
||||
a.attnum
|
||||
FROM pg_attribute a
|
||||
INNER JOIN pg_class c ON (a.attrelid=c.oid)
|
||||
INNER JOIN pg_type t ON (a.atttypid=t.oid)
|
||||
INNER JOIN pg_namespace n ON (c.relnamespace=n.oid)
|
||||
WHERE t.typname='geometry'
|
||||
AND c.relkind='r'
|
||||
) g
|
||||
LEFT JOIN pg_index i ON (g.relid = i.indrelid AND g.attnum = ANY(i.indkey))
|
||||
WHERE relname = '{}' AND attname = '{}'{};
|
||||
""".format(
|
||||
table.name, column_info["name"], schema_part
|
||||
)
|
||||
spatial_index = inspector.bind.execute(text(has_index_query)).scalar()
|
||||
|
||||
# Set attributes
|
||||
column_info["type"].geometry_type = geometry_type
|
||||
column_info["type"].dimension = coord_dimension
|
||||
column_info["type"].spatial_index = bool(spatial_index)
|
||||
|
||||
# Spatial indexes are automatically reflected with PostgreSQL dialect
|
||||
column_info["type"]._spatial_index_reflected = True
|
||||
|
||||
|
||||
def before_create(table, bind, **kw):
|
||||
"""Handle spatial indexes during the before_create event."""
|
||||
dialect, gis_cols, regular_cols = setup_create_drop(table, bind, check_management)
|
||||
|
||||
# Remove the spatial indexes from the table metadata because they should not be
|
||||
# created during the table.create() step since the associated columns do not exist
|
||||
# at this time.
|
||||
table.info["_after_create_indexes"] = []
|
||||
current_indexes = set(table.indexes)
|
||||
for idx in current_indexes:
|
||||
for col in table.info["_saved_columns"]:
|
||||
if (
|
||||
_check_spatial_type(col.type, Geometry, dialect) and check_management(col)
|
||||
) and col in idx.columns.values():
|
||||
table.indexes.remove(idx)
|
||||
if idx.name != _spatial_idx_name(table.name, col.name) or not getattr(
|
||||
col.type, "spatial_index", False
|
||||
):
|
||||
table.info["_after_create_indexes"].append(idx)
|
||||
|
||||
|
||||
def after_create(table, bind, **kw):
|
||||
"""Handle spatial indexes during the after_create event."""
|
||||
# Restore original column list including managed Geometry columns
|
||||
dialect = bind.dialect
|
||||
|
||||
table.columns = table.info.pop("_saved_columns")
|
||||
|
||||
for col in table.columns:
|
||||
# Add the managed Geometry columns with AddGeometryColumn()
|
||||
if _check_spatial_type(col.type, Geometry, dialect) and check_management(col):
|
||||
dimension = col.type.dimension
|
||||
args = [table.schema] if table.schema else []
|
||||
args.extend([table.name, col.name, col.type.srid, col.type.geometry_type, dimension])
|
||||
if col.type.use_typmod is not None:
|
||||
args.append(col.type.use_typmod)
|
||||
|
||||
stmt = select(*_format_select_args(func.AddGeometryColumn(*args)))
|
||||
stmt = stmt.execution_options(autocommit=True)
|
||||
bind.execute(stmt)
|
||||
|
||||
# Add spatial indices for the Geometry and Geography columns
|
||||
if (
|
||||
_check_spatial_type(col.type, (Geometry, Geography), dialect)
|
||||
and col.type.spatial_index is True
|
||||
):
|
||||
# If the index does not exist, define it and create it
|
||||
if not [i for i in table.indexes if col in i.columns.values()] and check_management(
|
||||
col
|
||||
):
|
||||
create_spatial_index(bind, table, col)
|
||||
|
||||
for idx in table.info.pop("_after_create_indexes"):
|
||||
table.indexes.add(idx)
|
||||
idx.create(bind=bind)
|
||||
|
||||
|
||||
def before_drop(table, bind, **kw):
|
||||
"""Handle spatial indexes during the before_drop event."""
|
||||
dialect, gis_cols, regular_cols = setup_create_drop(table, bind, check_management)
|
||||
|
||||
# Drop the managed Geometry columns
|
||||
for col in gis_cols:
|
||||
args = [table.schema] if table.schema else []
|
||||
args.extend([table.name, col.name])
|
||||
|
||||
stmt = select(*_format_select_args(func.DropGeometryColumn(*args)))
|
||||
stmt = stmt.execution_options(autocommit=True)
|
||||
bind.execute(stmt)
|
||||
|
||||
|
||||
def after_drop(table, bind, **kw):
|
||||
"""Handle spatial indexes during the after_drop event."""
|
||||
# Restore original column list including managed Geometry columns
|
||||
saved_cols = table.info.pop("_saved_columns", None)
|
||||
if saved_cols is not None:
|
||||
table.columns = saved_cols
|
||||
@@ -0,0 +1,369 @@
|
||||
"""This module defines specific functions for SQLite dialect."""
|
||||
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.compiler import compiles
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.sql import select
|
||||
|
||||
from geoalchemy2 import functions
|
||||
from geoalchemy2.admin.dialects.common import _check_spatial_type
|
||||
from geoalchemy2.admin.dialects.common import _format_select_args
|
||||
from geoalchemy2.admin.dialects.common import _spatial_idx_name
|
||||
from geoalchemy2.admin.dialects.common import setup_create_drop
|
||||
from geoalchemy2.types import Geography
|
||||
from geoalchemy2.types import Geometry
|
||||
from geoalchemy2.types import _DummyGeometry
|
||||
from geoalchemy2.utils import authorized_values_in_docstring
|
||||
|
||||
|
||||
def load_spatialite_driver(dbapi_conn, *args):
|
||||
"""Load SpatiaLite extension in SQLite connection.
|
||||
|
||||
.. Warning::
|
||||
The path to the SpatiaLite module should be set in the `SPATIALITE_LIBRARY_PATH`
|
||||
environment variable.
|
||||
|
||||
Args:
|
||||
dbapi_conn: The DBAPI connection.
|
||||
"""
|
||||
if "SPATIALITE_LIBRARY_PATH" not in os.environ:
|
||||
raise RuntimeError("The SPATIALITE_LIBRARY_PATH environment variable is not set.")
|
||||
dbapi_conn.enable_load_extension(True)
|
||||
dbapi_conn.load_extension(os.environ["SPATIALITE_LIBRARY_PATH"])
|
||||
dbapi_conn.enable_load_extension(False)
|
||||
|
||||
|
||||
_JOURNAL_MODE_VALUES = ["DELETE", "TRUNCATE", "PERSIST", "MEMORY", "WAL", "OFF"]
|
||||
|
||||
|
||||
@authorized_values_in_docstring(JOURNAL_MODE_VALUES=_JOURNAL_MODE_VALUES)
|
||||
def init_spatialite(
|
||||
dbapi_conn,
|
||||
*args,
|
||||
transaction: bool = False,
|
||||
init_mode: Optional[str] = None,
|
||||
journal_mode: Optional[str] = None,
|
||||
):
|
||||
"""Initialize internal SpatiaLite tables.
|
||||
|
||||
Args:
|
||||
dbapi_conn: The DBAPI connection.
|
||||
init_mode: Can be `None` to load all EPSG SRIDs, `'WGS84'` to load only the ones related
|
||||
to WGS84 or `'EMPTY'` to not load any EPSG SRID.
|
||||
|
||||
.. Note::
|
||||
|
||||
It is possible to load other EPSG SRIDs afterwards using `InsertEpsgSrid(srid)`.
|
||||
|
||||
transaction: If set to `True` the whole operation will be handled as a single Transaction
|
||||
(faster). The default value is `False` (slower, but safer).
|
||||
journal_mode: Change the journal mode to the given value. This can make the table creation
|
||||
much faster. The possible values are the following: <JOURNAL_MODE_VALUES>. See
|
||||
https://www.sqlite.org/pragma.html#pragma_journal_mode for more details.
|
||||
|
||||
.. Warning::
|
||||
Some values, like 'MEMORY' or 'OFF', can lead to corrupted databases if the process
|
||||
is interrupted during initialization.
|
||||
|
||||
.. Note::
|
||||
The original value is restored after the initialization.
|
||||
|
||||
.. Note::
|
||||
When using this function as a listener it is not possible to pass the `transaction`,
|
||||
`init_mode` or `journal_mode` arguments directly. To do this you can either create another
|
||||
function that calls `init_spatialite` (or
|
||||
:func:`geoalchemy2.admin.dialects.sqlite.load_spatialite` if you also want to load the
|
||||
SpatiaLite drivers) with an hard-coded `init_mode` or just use a lambda::
|
||||
|
||||
>>> sqlalchemy.event.listen(
|
||||
... engine,
|
||||
... "connect",
|
||||
... lambda x, y: init_spatialite(
|
||||
... x,
|
||||
... y,
|
||||
... transaction=True,
|
||||
... init_mode="EMPTY",
|
||||
... journal_mode="OFF",
|
||||
... )
|
||||
... )
|
||||
"""
|
||||
func_args = []
|
||||
|
||||
# Check the value of the 'transaction' parameter
|
||||
if not isinstance(transaction, (bool, int)):
|
||||
raise ValueError("The 'transaction' argument must be True or False.")
|
||||
else:
|
||||
func_args.append(str(transaction))
|
||||
|
||||
# Check the value of the 'init_mode' parameter
|
||||
init_mode_values = ["WGS84", "EMPTY"]
|
||||
if isinstance(init_mode, str):
|
||||
init_mode = init_mode.upper()
|
||||
if init_mode is not None:
|
||||
if init_mode not in init_mode_values:
|
||||
raise ValueError("The 'init_mode' argument must be one of {}.".format(init_mode_values))
|
||||
func_args.append(f"'{init_mode}'")
|
||||
|
||||
# Check the value of the 'journal_mode' parameter
|
||||
if isinstance(journal_mode, str):
|
||||
journal_mode = journal_mode.upper()
|
||||
if journal_mode is not None:
|
||||
if journal_mode not in _JOURNAL_MODE_VALUES:
|
||||
raise ValueError(
|
||||
"The 'journal_mode' argument must be one of {}.".format(_JOURNAL_MODE_VALUES)
|
||||
)
|
||||
|
||||
if dbapi_conn.execute("SELECT CheckSpatialMetaData();").fetchone()[0] < 1:
|
||||
if journal_mode is not None:
|
||||
current_journal_mode = dbapi_conn.execute("PRAGMA journal_mode").fetchone()[0]
|
||||
dbapi_conn.execute("PRAGMA journal_mode = {}".format(journal_mode))
|
||||
|
||||
dbapi_conn.execute("SELECT InitSpatialMetaData({});".format(", ".join(func_args)))
|
||||
|
||||
if journal_mode is not None:
|
||||
dbapi_conn.execute("PRAGMA journal_mode = {}".format(current_journal_mode))
|
||||
|
||||
|
||||
def load_spatialite(*args, **kwargs):
|
||||
"""Load SpatiaLite extension in SQLite DB and initialize internal tables.
|
||||
|
||||
See :func:`geoalchemy2.admin.dialects.sqlite.load_spatialite_driver` and
|
||||
:func:`geoalchemy2.admin.dialects.sqlite.init_spatialite` functions for details about
|
||||
arguments.
|
||||
"""
|
||||
load_spatialite_driver(*args)
|
||||
init_spatialite(*args, **kwargs)
|
||||
|
||||
|
||||
def _get_spatialite_attrs(bind, table_name, col_name):
|
||||
attrs = bind.execute(
|
||||
text(
|
||||
"""SELECT * FROM "geometry_columns"
|
||||
WHERE LOWER(f_table_name) = LOWER(:table_name)
|
||||
AND LOWER(f_geometry_column) = LOWER(:column_name)
|
||||
"""
|
||||
).bindparams(table_name=table_name, column_name=col_name)
|
||||
).fetchone()
|
||||
if attrs is None:
|
||||
# If the column is not registered as a spatial column we ignore it
|
||||
return None
|
||||
return attrs[2:]
|
||||
|
||||
|
||||
def get_spatialite_version(bind):
|
||||
"""Get the version of the currently loaded Spatialite extension."""
|
||||
return bind.execute(text("SELECT spatialite_version();")).fetchone()[0]
|
||||
|
||||
|
||||
def _setup_dummy_type(table, gis_cols):
|
||||
"""Setup dummy type for new Geometry columns so they can be updated later."""
|
||||
for col in gis_cols:
|
||||
# Add dummy columns with GEOMETRY type
|
||||
col._actual_type = col.type
|
||||
col.type = _DummyGeometry()
|
||||
table.columns = table.info["_saved_columns"]
|
||||
|
||||
|
||||
def get_col_dim(col):
|
||||
"""Get dimension of the column type."""
|
||||
if col.type.dimension == 4:
|
||||
dimension = "XYZM"
|
||||
elif col.type.dimension == 2:
|
||||
dimension = "XY"
|
||||
else:
|
||||
if col.type.geometry_type.endswith("M"):
|
||||
dimension = "XYM"
|
||||
else:
|
||||
dimension = "XYZ"
|
||||
return dimension
|
||||
|
||||
|
||||
def create_spatial_index(bind, table, col):
|
||||
"""Create spatial index on the given column."""
|
||||
stmt = select(*_format_select_args(func.CreateSpatialIndex(table.name, col.name)))
|
||||
stmt = stmt.execution_options(autocommit=True)
|
||||
bind.execute(stmt)
|
||||
|
||||
|
||||
def disable_spatial_index(bind, table, col):
|
||||
"""Disable spatial indexes if present."""
|
||||
stmt = select(*_format_select_args(func.CheckSpatialIndex(table.name, col.name)))
|
||||
if bind.execute(stmt).fetchone()[0] is not None:
|
||||
stmt = select(*_format_select_args(func.DisableSpatialIndex(table.name, col.name)))
|
||||
stmt = stmt.execution_options(autocommit=True)
|
||||
bind.execute(stmt)
|
||||
bind.execute(
|
||||
text(
|
||||
"DROP TABLE IF EXISTS {};".format(
|
||||
_spatial_idx_name(
|
||||
table.name,
|
||||
col.name,
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def reflect_geometry_column(inspector, table, column_info):
|
||||
"""Reflect a column of type Geometry with SQLite dialect."""
|
||||
# Get geometry type, SRID and spatial index from the SpatiaLite metadata
|
||||
if not isinstance(column_info.get("type"), Geometry):
|
||||
return
|
||||
col_attributes = _get_spatialite_attrs(inspector.bind, table.name, column_info["name"])
|
||||
if col_attributes is not None:
|
||||
geometry_type, coord_dimension, srid, spatial_index = col_attributes
|
||||
|
||||
if isinstance(geometry_type, int):
|
||||
geometry_type_str = str(geometry_type)
|
||||
if geometry_type >= 1000:
|
||||
first_digit = geometry_type_str[0]
|
||||
has_z = first_digit in ["1", "3"]
|
||||
has_m = first_digit in ["2", "3"]
|
||||
else:
|
||||
has_z = has_m = False
|
||||
geometry_type = {
|
||||
"0": "GEOMETRY",
|
||||
"1": "POINT",
|
||||
"2": "LINESTRING",
|
||||
"3": "POLYGON",
|
||||
"4": "MULTIPOINT",
|
||||
"5": "MULTILINESTRING",
|
||||
"6": "MULTIPOLYGON",
|
||||
"7": "GEOMETRYCOLLECTION",
|
||||
}[geometry_type_str[-1]]
|
||||
if has_z:
|
||||
geometry_type += "Z"
|
||||
if has_m:
|
||||
geometry_type += "M"
|
||||
else:
|
||||
if "Z" in coord_dimension and "Z" not in geometry_type[-2:]:
|
||||
geometry_type += "Z"
|
||||
if "M" in coord_dimension and "M" not in geometry_type[-2:]:
|
||||
geometry_type += "M"
|
||||
coord_dimension = {
|
||||
"XY": 2,
|
||||
"XYZ": 3,
|
||||
"XYM": 3,
|
||||
"XYZM": 4,
|
||||
}.get(coord_dimension, coord_dimension)
|
||||
|
||||
# Set attributes
|
||||
column_info["type"].geometry_type = geometry_type
|
||||
column_info["type"].dimension = coord_dimension
|
||||
column_info["type"].srid = srid
|
||||
column_info["type"].spatial_index = bool(spatial_index)
|
||||
|
||||
# Spatial indexes are not automatically reflected with SQLite dialect
|
||||
column_info["type"]._spatial_index_reflected = False
|
||||
|
||||
|
||||
def before_create(table, bind, **kw):
|
||||
"""Handle spatial indexes during the before_create event."""
|
||||
dialect, gis_cols, regular_cols = setup_create_drop(table, bind)
|
||||
|
||||
# Remove the spatial indexes from the table metadata because they should not be
|
||||
# created during the table.create() step since the associated columns do not exist
|
||||
# at this time.
|
||||
table.info["_after_create_indexes"] = []
|
||||
current_indexes = set(table.indexes)
|
||||
for idx in current_indexes:
|
||||
for col in table.info["_saved_columns"]:
|
||||
if (_check_spatial_type(col.type, Geometry, dialect)) and col in idx.columns.values():
|
||||
table.indexes.remove(idx)
|
||||
if idx.name != _spatial_idx_name(table.name, col.name) or not getattr(
|
||||
col.type, "spatial_index", False
|
||||
):
|
||||
table.info["_after_create_indexes"].append(idx)
|
||||
|
||||
_setup_dummy_type(table, gis_cols)
|
||||
|
||||
|
||||
def after_create(table, bind, **kw):
|
||||
"""Handle spatial indexes during the after_create event."""
|
||||
dialect = bind.dialect
|
||||
|
||||
table.columns = table.info.pop("_saved_columns")
|
||||
for col in table.columns:
|
||||
# Add the managed Geometry columns with RecoverGeometryColumn()
|
||||
if _check_spatial_type(col.type, Geometry, dialect):
|
||||
col.type = col._actual_type
|
||||
del col._actual_type
|
||||
dimension = get_col_dim(col)
|
||||
args = [table.name, col.name, col.type.srid, col.type.geometry_type, dimension]
|
||||
|
||||
stmt = select(*_format_select_args(func.RecoverGeometryColumn(*args)))
|
||||
stmt = stmt.execution_options(autocommit=True)
|
||||
bind.execute(stmt)
|
||||
|
||||
for col in table.columns:
|
||||
# Add spatial indexes for the Geometry and Geography columns
|
||||
# TODO: Check that the Geography type makes sense here
|
||||
if (
|
||||
_check_spatial_type(col.type, (Geometry, Geography), dialect)
|
||||
and col.type.spatial_index is True
|
||||
):
|
||||
create_spatial_index(bind, table, col)
|
||||
|
||||
for idx in table.info.pop("_after_create_indexes"):
|
||||
table.indexes.add(idx)
|
||||
idx.create(bind=bind)
|
||||
|
||||
|
||||
def before_drop(table, bind, **kw):
|
||||
"""Handle spatial indexes during the before_drop event."""
|
||||
dialect, gis_cols, regular_cols = setup_create_drop(table, bind)
|
||||
|
||||
for col in gis_cols:
|
||||
# Disable spatial indexes if present
|
||||
disable_spatial_index(bind, table, col)
|
||||
|
||||
args = [table.name, col.name]
|
||||
|
||||
stmt = select(*_format_select_args(func.DiscardGeometryColumn(*args)))
|
||||
stmt = stmt.execution_options(autocommit=True)
|
||||
bind.execute(stmt)
|
||||
|
||||
|
||||
def after_drop(table, bind, **kw):
|
||||
"""Handle spatial indexes during the after_drop event."""
|
||||
table.columns = table.info.pop("_saved_columns")
|
||||
|
||||
|
||||
# Define compiled versions for functions in SpatiaLite whose names don't have
|
||||
# the ST_ prefix.
|
||||
_SQLITE_FUNCTIONS = {
|
||||
"ST_GeomFromEWKT": "GeomFromEWKT",
|
||||
"ST_GeomFromEWKB": "GeomFromEWKB",
|
||||
"ST_AsBinary": "AsBinary",
|
||||
"ST_AsEWKB": "AsEWKB",
|
||||
"ST_AsGeoJSON": "AsGeoJSON",
|
||||
}
|
||||
|
||||
|
||||
def _compiles_sqlite(cls, fn):
|
||||
def _compile_sqlite(element, compiler, **kw):
|
||||
return "{}({})".format(fn, compiler.process(element.clauses, **kw))
|
||||
|
||||
compiles(getattr(functions, cls), "sqlite")(_compile_sqlite)
|
||||
|
||||
|
||||
def register_sqlite_mapping(mapping):
|
||||
"""Register compilation mappings for the given functions.
|
||||
|
||||
Args:
|
||||
mapping: Should have the following form::
|
||||
|
||||
{
|
||||
"function_name_1": "sqlite_function_name_1",
|
||||
"function_name_2": "sqlite_function_name_2",
|
||||
...
|
||||
}
|
||||
"""
|
||||
for cls, fn in mapping.items():
|
||||
_compiles_sqlite(cls, fn)
|
||||
|
||||
|
||||
register_sqlite_mapping(_SQLITE_FUNCTIONS)
|
||||
@@ -0,0 +1,779 @@
|
||||
"""Some helpers to use with Alembic migration tool."""
|
||||
|
||||
from alembic.autogenerate import renderers
|
||||
from alembic.autogenerate import rewriter
|
||||
from alembic.autogenerate.render import _add_column
|
||||
from alembic.autogenerate.render import _add_index
|
||||
from alembic.autogenerate.render import _add_table
|
||||
from alembic.autogenerate.render import _drop_column
|
||||
from alembic.autogenerate.render import _drop_index
|
||||
from alembic.autogenerate.render import _drop_table
|
||||
from alembic.ddl.base import RenameTable
|
||||
from alembic.ddl.base import format_table_name
|
||||
from alembic.ddl.base import visit_rename_table
|
||||
from alembic.ddl.sqlite import SQLiteImpl
|
||||
from alembic.operations import BatchOperations
|
||||
from alembic.operations import Operations
|
||||
from alembic.operations import ops
|
||||
from sqlalchemy import Column
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.dialects.mysql.base import MySQLDialect
|
||||
from sqlalchemy.dialects.sqlite.base import SQLiteDialect
|
||||
from sqlalchemy.ext.compiler import compiles
|
||||
from sqlalchemy.schema import DropTable
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.types import TypeDecorator
|
||||
|
||||
from geoalchemy2 import Geography
|
||||
from geoalchemy2 import Geometry
|
||||
from geoalchemy2 import Raster
|
||||
from geoalchemy2.admin.dialects.common import _check_spatial_type
|
||||
from geoalchemy2.admin.dialects.common import _get_gis_cols
|
||||
from geoalchemy2.admin.dialects.common import _spatial_idx_name
|
||||
|
||||
writer = rewriter.Rewriter()
|
||||
"""Rewriter object for Alembic."""
|
||||
|
||||
_SPATIAL_TABLES = set()
|
||||
|
||||
|
||||
class GeoPackageImpl(SQLiteImpl):
|
||||
"""Class to copy the Alembic implementation from SQLite to GeoPackage."""
|
||||
|
||||
__dialect__ = "geopackage"
|
||||
|
||||
|
||||
def _monkey_patch_get_indexes_for_sqlite():
|
||||
"""Monkey patch SQLAlchemy to fix spatial index reflection."""
|
||||
normal_behavior = SQLiteDialect.get_indexes
|
||||
|
||||
def spatial_behavior(self, connection, table_name, schema=None, **kw):
|
||||
indexes = self._get_indexes_normal_behavior(connection, table_name, schema=None, **kw)
|
||||
|
||||
is_gpkg = connection.dialect.name == "geopackage"
|
||||
|
||||
try:
|
||||
# Check that SpatiaLite was loaded into the DB
|
||||
is_spatial_db = connection.exec_driver_sql(
|
||||
"""PRAGMA main.table_info({})""".format(
|
||||
"gpkg_geometry_columns" if is_gpkg else "geometry_columns"
|
||||
)
|
||||
).fetchall()
|
||||
if not is_spatial_db:
|
||||
return indexes
|
||||
except AttributeError:
|
||||
return indexes
|
||||
|
||||
# Get spatial indexes
|
||||
if is_gpkg:
|
||||
spatial_index_query = text(
|
||||
"""SELECT A.table_name, A.column_name, IFNULL(B.has_index, 0) AS has_index
|
||||
FROM "gpkg_geometry_columns"
|
||||
AS A
|
||||
LEFT JOIN (
|
||||
SELECT table_name, column_name, COUNT(*) AS has_index
|
||||
FROM gpkg_extensions
|
||||
WHERE LOWER(table_name) = LOWER('{table_name}')
|
||||
AND extension_name = 'gpkg_rtree_index'
|
||||
) AS B
|
||||
ON LOWER(A.table_name) = LOWER(B.table_name)
|
||||
WHERE LOWER(A.table_name) = LOWER('{table_name}');
|
||||
""".format(
|
||||
table_name=table_name
|
||||
)
|
||||
)
|
||||
else:
|
||||
spatial_index_query = text(
|
||||
"""SELECT *
|
||||
FROM geometry_columns
|
||||
WHERE f_table_name = '{}'
|
||||
ORDER BY f_table_name, f_geometry_column;""".format(
|
||||
table_name
|
||||
)
|
||||
)
|
||||
|
||||
spatial_indexes = connection.execute(spatial_index_query).fetchall()
|
||||
|
||||
if spatial_indexes:
|
||||
reflected_names = set([i["name"] for i in indexes])
|
||||
for idx in spatial_indexes:
|
||||
idx_col = idx[1]
|
||||
idx_name = _spatial_idx_name(table_name, idx_col)
|
||||
if not bool(idx[-1]) or idx_name in reflected_names:
|
||||
continue
|
||||
indexes.append(
|
||||
{
|
||||
"name": idx_name,
|
||||
"column_names": [idx_col],
|
||||
"unique": 0,
|
||||
"dialect_options": {"_column_flag": True},
|
||||
}
|
||||
)
|
||||
reflected_names.add(idx_name)
|
||||
|
||||
return indexes
|
||||
|
||||
spatial_behavior.__doc__ = normal_behavior.__doc__
|
||||
SQLiteDialect.get_indexes = spatial_behavior
|
||||
SQLiteDialect._get_indexes_normal_behavior = normal_behavior
|
||||
|
||||
|
||||
_monkey_patch_get_indexes_for_sqlite()
|
||||
|
||||
|
||||
def _monkey_patch_get_indexes_for_mysql():
|
||||
"""Monkey patch SQLAlchemy to fix spatial index reflection."""
|
||||
normal_behavior = MySQLDialect.get_indexes
|
||||
|
||||
def spatial_behavior(self, connection, table_name, schema=None, **kw):
|
||||
indexes = self._get_indexes_normal_behavior(connection, table_name, schema=None, **kw)
|
||||
|
||||
# Get spatial indexes
|
||||
has_index_query = """SELECT DISTINCT
|
||||
COLUMN_NAME
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_NAME = '{}' AND INDEX_TYPE = 'SPATIAL'""".format(
|
||||
table_name
|
||||
)
|
||||
if schema is not None:
|
||||
has_index_query += """ AND TABLE_SCHEMA = '{}'""".format(schema)
|
||||
spatial_indexes = connection.execute(text(has_index_query)).fetchall()
|
||||
|
||||
if spatial_indexes:
|
||||
reflected_names = set([i["name"] for i in indexes])
|
||||
for idx in spatial_indexes:
|
||||
idx_col = idx[0]
|
||||
idx_name = _spatial_idx_name(table_name, idx_col)
|
||||
if idx_name in reflected_names:
|
||||
continue
|
||||
indexes.append(
|
||||
{
|
||||
"name": idx_name,
|
||||
"column_names": [idx_col],
|
||||
"unique": 0,
|
||||
"dialect_options": {"_column_flag": True},
|
||||
}
|
||||
)
|
||||
reflected_names.add(idx_name)
|
||||
|
||||
return indexes
|
||||
|
||||
spatial_behavior.__doc__ = normal_behavior.__doc__
|
||||
MySQLDialect.get_indexes = spatial_behavior
|
||||
MySQLDialect._get_indexes_normal_behavior = normal_behavior
|
||||
|
||||
|
||||
_monkey_patch_get_indexes_for_mysql()
|
||||
|
||||
|
||||
def render_item(obj_type, obj, autogen_context):
|
||||
"""Add proper imports for spatial types."""
|
||||
if obj_type == "type" and isinstance(obj, (Geometry, Geography, Raster)):
|
||||
import_name = obj.__class__.__name__
|
||||
autogen_context.imports.add(f"from geoalchemy2 import {import_name}")
|
||||
return "%r" % obj
|
||||
|
||||
# Default rendering for other objects
|
||||
return False
|
||||
|
||||
|
||||
def include_object(obj, name, obj_type, reflected, compare_to):
|
||||
"""Do not include internal tables of spatial extensions.
|
||||
|
||||
.. warning::
|
||||
This function only checks the table names, so it might exclude tables that should not be.
|
||||
In such case, you should create your own function to handle your specific table names.
|
||||
|
||||
"""
|
||||
if obj_type == "table" and (
|
||||
name.startswith("geometry_columns")
|
||||
or name.startswith("spatial_ref_sys")
|
||||
or name.startswith("spatialite_history")
|
||||
or name.startswith("sqlite_sequence")
|
||||
or name.startswith("views_geometry_columns")
|
||||
or name.startswith("virts_geometry_columns")
|
||||
or name.startswith("idx_")
|
||||
or name.startswith("gpkg_")
|
||||
or name.startswith("vgpkg_")
|
||||
):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@Operations.register_operation("add_geospatial_column")
|
||||
@BatchOperations.register_operation("add_geospatial_column", "batch_add_geospatial_column")
|
||||
class AddGeospatialColumnOp(ops.AddColumnOp):
|
||||
"""Add a Geospatial Column in an Alembic migration context.
|
||||
|
||||
This method originates from:
|
||||
https://alembic.sqlalchemy.org/en/latest/api/operations.html#operation-plugins
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def add_geospatial_column(cls, operations, table_name, column, schema=None):
|
||||
"""Handle the different situations arising from adding geospatial column to a DB."""
|
||||
op = cls(table_name, column, schema=schema)
|
||||
return operations.invoke(op)
|
||||
|
||||
def reverse(self):
|
||||
"""Used to autogenerate the downgrade function."""
|
||||
return DropGeospatialColumnOp.from_column_and_tablename(
|
||||
self.schema, self.table_name, self.column.name
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def batch_add_geospatial_column(
|
||||
cls,
|
||||
operations,
|
||||
column,
|
||||
insert_before=None,
|
||||
insert_after=None,
|
||||
):
|
||||
"""Issue an "add column" instruction using the current batch migration context."""
|
||||
kw = {}
|
||||
if insert_before:
|
||||
kw["insert_before"] = insert_before
|
||||
if insert_after:
|
||||
kw["insert_after"] = insert_after
|
||||
|
||||
op = cls(
|
||||
operations.impl.table_name,
|
||||
column,
|
||||
schema=operations.impl.schema,
|
||||
**kw,
|
||||
)
|
||||
return operations.invoke(op)
|
||||
|
||||
|
||||
@Operations.register_operation("drop_geospatial_column")
|
||||
@BatchOperations.register_operation("drop_geospatial_column", "batch_drop_geospatial_column")
|
||||
class DropGeospatialColumnOp(ops.DropColumnOp):
|
||||
"""Drop a Geospatial Column in an Alembic migration context."""
|
||||
|
||||
@classmethod
|
||||
def drop_geospatial_column(cls, operations, table_name, column_name, schema=None, **kw):
|
||||
"""Handle the different situations arising from dropping geospatial column from a DB."""
|
||||
op = cls(table_name, column_name, schema=schema, **kw)
|
||||
return operations.invoke(op)
|
||||
|
||||
def reverse(self):
|
||||
"""Used to autogenerate the downgrade function."""
|
||||
return AddGeospatialColumnOp.from_column_and_tablename(
|
||||
self.schema, self.table_name, self.column
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def batch_drop_geospatial_column(cls, operations, column_name, **kw):
|
||||
"""Issue a "drop column" instruction using the current batch migration context."""
|
||||
op = cls(
|
||||
operations.impl.table_name,
|
||||
column_name,
|
||||
schema=operations.impl.schema,
|
||||
**kw,
|
||||
)
|
||||
return operations.invoke(op)
|
||||
|
||||
|
||||
@Operations.implementation_for(AddGeospatialColumnOp)
|
||||
def add_geospatial_column(operations, operation):
|
||||
"""Handle the actual column addition according to the dialect backend.
|
||||
|
||||
Args:
|
||||
operations: Operations object from alembic base, defining high level migration operations.
|
||||
operation: AddGeospatialColumnOp call, with attributes for table_name, column_name,
|
||||
column_type, and optional keywords.
|
||||
"""
|
||||
table_name = operation.table_name
|
||||
column_name = operation.column.name
|
||||
|
||||
dialect = operations.get_bind().dialect
|
||||
|
||||
if dialect.name == "sqlite":
|
||||
if isinstance(operation.column, TypeDecorator):
|
||||
# Will be either geoalchemy2.types.Geography or geoalchemy2.types.Geometry, if using a
|
||||
# custom type
|
||||
geospatial_core_type = operation.column.type.load_dialect_impl(dialect)
|
||||
else:
|
||||
geospatial_core_type = operation.column.type
|
||||
operations.execute(
|
||||
func.AddGeometryColumn(
|
||||
table_name,
|
||||
column_name,
|
||||
geospatial_core_type.srid,
|
||||
geospatial_core_type.geometry_type,
|
||||
geospatial_core_type.dimension,
|
||||
not geospatial_core_type.nullable,
|
||||
)
|
||||
)
|
||||
elif "postgresql" in dialect.name:
|
||||
operations.impl.add_column(
|
||||
table_name,
|
||||
operation.column,
|
||||
schema=operation.schema,
|
||||
)
|
||||
|
||||
|
||||
@Operations.implementation_for(DropGeospatialColumnOp)
|
||||
def drop_geospatial_column(operations, operation):
|
||||
"""Handle the actual column removal according to the dialect backend.
|
||||
|
||||
Args:
|
||||
operations: Operations object from alembic base, defining high level migration operations.
|
||||
operation: AddGeospatialColumnOp call, with attributes for table_name, column_name,
|
||||
column_type, and optional keywords.
|
||||
"""
|
||||
table_name = operation.table_name
|
||||
column = operation.to_column(operations.migration_context)
|
||||
|
||||
dialect = operations.get_bind().dialect
|
||||
|
||||
if dialect.name == "sqlite":
|
||||
_SPATIAL_TABLES.add(table_name)
|
||||
operations.impl.drop_column(table_name, column, schema=operation.schema, **operation.kw)
|
||||
|
||||
|
||||
@compiles(RenameTable, "sqlite")
|
||||
def visit_rename_geospatial_table(element, compiler, **kw):
|
||||
"""Specific compilation rule to rename spatial tables with SQLite dialect."""
|
||||
table_is_spatial = element.table_name in _SPATIAL_TABLES
|
||||
new_table_is_spatial = element.new_table_name in _SPATIAL_TABLES
|
||||
|
||||
if table_is_spatial or new_table_is_spatial:
|
||||
# Here we suppose there is only one DB attached to the current engine, so the prefix
|
||||
# is set to NULL
|
||||
return "SELECT RenameTable(NULL, '%s', '%s')" % (
|
||||
format_table_name(compiler, element.table_name, element.schema),
|
||||
format_table_name(compiler, element.new_table_name, element.schema),
|
||||
)
|
||||
else:
|
||||
return visit_rename_table(element, compiler, **kw)
|
||||
|
||||
|
||||
@compiles(DropTable, "sqlite")
|
||||
def visit_drop_geospatial_table(element, compiler, **kw):
|
||||
"""Specific compilation rule to drop spatial tables with SQLite dialect."""
|
||||
table_is_spatial = element.element.name in _SPATIAL_TABLES
|
||||
|
||||
if table_is_spatial:
|
||||
# Here we suppose there is only one DB attached to the current engine
|
||||
return "SELECT DropTable(NULL, '%s')" % (
|
||||
format_table_name(compiler, element.element.name, None),
|
||||
)
|
||||
else:
|
||||
return compiler.visit_drop_table(element, **kw)
|
||||
|
||||
|
||||
@renderers.dispatch_for(AddGeospatialColumnOp)
|
||||
def render_add_geo_column(autogen_context, op):
|
||||
"""Render the add_geospatial_column operation in migration script."""
|
||||
col_render = _add_column(autogen_context, op)
|
||||
return col_render.replace(".add_column(", ".add_geospatial_column(")
|
||||
|
||||
|
||||
@renderers.dispatch_for(DropGeospatialColumnOp)
|
||||
def render_drop_geo_column(autogen_context, op):
|
||||
"""Render the drop_geospatial_column operation in migration script."""
|
||||
col_render = _drop_column(autogen_context, op)
|
||||
return col_render.replace(".drop_column(", ".drop_geospatial_column(")
|
||||
|
||||
|
||||
@writer.rewrites(ops.AddColumnOp)
|
||||
def add_geo_column(context, revision, op):
|
||||
"""Replace the default AddColumnOp by a geospatial-specific one."""
|
||||
col_type = op.column.type
|
||||
if isinstance(col_type, TypeDecorator):
|
||||
dialect = context.bind.dialect
|
||||
col_type = col_type.load_dialect_impl(dialect)
|
||||
if isinstance(col_type, (Geometry, Geography, Raster)):
|
||||
op.column.type.spatial_index = False
|
||||
op.column.type._spatial_index_reflected = None
|
||||
new_op = AddGeospatialColumnOp(op.table_name, op.column, schema=op.schema)
|
||||
else:
|
||||
new_op = op
|
||||
return new_op
|
||||
|
||||
|
||||
@writer.rewrites(ops.DropColumnOp)
|
||||
def drop_geo_column(context, revision, op):
|
||||
"""Replace the default DropColumnOp by a geospatial-specific one."""
|
||||
col_type = op.to_column().type
|
||||
if isinstance(col_type, TypeDecorator):
|
||||
dialect = context.bind.dialect
|
||||
col_type = col_type.load_dialect_impl(dialect)
|
||||
if isinstance(col_type, (Geometry, Geography, Raster)):
|
||||
new_op = DropGeospatialColumnOp(op.table_name, op.column_name, schema=op.schema)
|
||||
else:
|
||||
new_op = op
|
||||
return new_op
|
||||
|
||||
|
||||
@Operations.register_operation("create_geospatial_table")
|
||||
class CreateGeospatialTableOp(ops.CreateTableOp):
|
||||
"""Create a Geospatial Table in an Alembic migration context.
|
||||
|
||||
This method originates from:
|
||||
https://alembic.sqlalchemy.org/en/latest/api/operations.html#operation-plugins
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def create_geospatial_table(cls, operations, table_name, *columns, **kw):
|
||||
"""Handle the different situations arising from creating geospatial table to a DB."""
|
||||
op = cls(table_name, columns, **kw)
|
||||
return operations.invoke(op)
|
||||
|
||||
def reverse(self):
|
||||
"""Used to autogenerate the downgrade function."""
|
||||
return DropGeospatialColumnOp.from_table(
|
||||
self.to_table(),
|
||||
_namespace_metadata=self._namespace_metadata,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_table(cls, table, _namespace_metadata=None):
|
||||
obj = super().from_table(table, _namespace_metadata)
|
||||
return obj
|
||||
|
||||
def to_table(self, migration_context=None):
|
||||
table = super().to_table(migration_context)
|
||||
|
||||
# Set spatial_index attribute to False so the indexes are created explicitly
|
||||
for col in table.columns:
|
||||
try:
|
||||
if col.type.spatial_index:
|
||||
col.type.spatial_index = False
|
||||
except AttributeError:
|
||||
pass
|
||||
return table
|
||||
|
||||
|
||||
@Operations.register_operation("drop_geospatial_table")
|
||||
class DropGeospatialTableOp(ops.DropTableOp):
|
||||
@classmethod
|
||||
def drop_geospatial_table(cls, operations, table_name, schema=None, **kw):
|
||||
"""Handle the different situations arising from dropping geospatial table from a DB."""
|
||||
op = cls(table_name, schema=schema, table_kw=kw)
|
||||
return operations.invoke(op)
|
||||
|
||||
def reverse(self):
|
||||
"""Used to autogenerate the downgrade function."""
|
||||
return CreateGeospatialTableOp.from_table(
|
||||
self.to_table(),
|
||||
_namespace_metadata=self._namespace_metadata,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_table(cls, table, _namespace_metadata=None):
|
||||
obj = super().from_table(table, _namespace_metadata)
|
||||
return obj
|
||||
|
||||
def to_table(self, migration_context=None):
|
||||
table = super().to_table(migration_context)
|
||||
return table
|
||||
|
||||
|
||||
@Operations.implementation_for(CreateGeospatialTableOp)
|
||||
def create_geospatial_table(operations, operation):
|
||||
"""Handle the actual table creation according to the dialect backend.
|
||||
|
||||
Args:
|
||||
operations: Operations object from alembic base, defining high level migration operations.
|
||||
operation: CreateGeospatialTableOp call, with attributes for table_name, column_name,
|
||||
column_type, and optional keywords.
|
||||
"""
|
||||
table_name = operation.table_name
|
||||
bind = operations.get_bind()
|
||||
|
||||
# For now the default events defined in geoalchemy2 are enough to handle table creation
|
||||
operations.create_table(table_name, *operation.columns, schema=operation.schema, **operation.kw)
|
||||
|
||||
if bind.dialect.name == "sqlite":
|
||||
_SPATIAL_TABLES.add(table_name)
|
||||
|
||||
|
||||
@Operations.implementation_for(DropGeospatialTableOp)
|
||||
def drop_geospatial_table(operations, operation):
|
||||
"""Handle the actual table removal according to the dialect backend.
|
||||
|
||||
Args:
|
||||
operations: Operations object from alembic base, defining high level migration operations.
|
||||
operation: DropGeospatialTableOp call, with attributes for table_name, column_name,
|
||||
column_type, and optional keywords.
|
||||
"""
|
||||
table_name = operation.table_name
|
||||
bind = operations.get_bind()
|
||||
dialect = bind.dialect
|
||||
|
||||
if dialect.name == "sqlite":
|
||||
_SPATIAL_TABLES.add(table_name)
|
||||
operations.drop_table(table_name, schema=operation.schema, **operation.table_kw)
|
||||
|
||||
|
||||
@renderers.dispatch_for(CreateGeospatialTableOp)
|
||||
def render_create_geo_table(autogen_context, op):
|
||||
"""Render the create_geospatial_table operation in migration script."""
|
||||
table_render = _add_table(autogen_context, op)
|
||||
return table_render.replace(".create_table(", ".create_geospatial_table(")
|
||||
|
||||
|
||||
@renderers.dispatch_for(DropGeospatialTableOp)
|
||||
def render_drop_geo_table(autogen_context, op):
|
||||
"""Render the drop_geospatial_table operation in migration script."""
|
||||
table_render = _drop_table(autogen_context, op)
|
||||
return table_render.replace(".drop_table(", ".drop_geospatial_table(")
|
||||
|
||||
|
||||
@writer.rewrites(ops.CreateTableOp)
|
||||
def create_geo_table(context, revision, op):
|
||||
"""Replace the default CreateTableOp by a geospatial-specific one."""
|
||||
dialect = context.bind.dialect
|
||||
gis_cols = _get_gis_cols(op, (Geometry, Geography, Raster), dialect)
|
||||
|
||||
if gis_cols:
|
||||
new_op = CreateGeospatialTableOp(
|
||||
op.table_name,
|
||||
op.columns,
|
||||
schema=op.schema,
|
||||
_namespace_metadata=op._namespace_metadata,
|
||||
_constraints_included=op._constraints_included,
|
||||
)
|
||||
else:
|
||||
new_op = op
|
||||
|
||||
return new_op
|
||||
|
||||
|
||||
@writer.rewrites(ops.DropTableOp)
|
||||
def drop_geo_table(context, revision, op):
|
||||
"""Replace the default DropTableOp by a geospatial-specific one."""
|
||||
dialect = context.bind.dialect
|
||||
table = op.to_table()
|
||||
gis_cols = _get_gis_cols(table, (Geometry, Geography, Raster), dialect)
|
||||
|
||||
if gis_cols:
|
||||
new_op = DropGeospatialTableOp(op.table_name, schema=op.schema)
|
||||
else:
|
||||
new_op = op
|
||||
|
||||
return new_op
|
||||
|
||||
|
||||
@Operations.register_operation("create_geospatial_index")
|
||||
@BatchOperations.register_operation("create_geospatial_index", "batch_create_geospatial_index")
|
||||
class CreateGeospatialIndexOp(ops.CreateIndexOp):
|
||||
@classmethod
|
||||
def create_geospatial_index(
|
||||
cls,
|
||||
operations,
|
||||
index_name,
|
||||
table_name,
|
||||
columns,
|
||||
schema=None,
|
||||
unique=False,
|
||||
**kw,
|
||||
):
|
||||
"""Handle the different situations arising from creating geospatial index into a DB."""
|
||||
op = cls(index_name, table_name, columns, schema=schema, unique=unique, **kw)
|
||||
return operations.invoke(op)
|
||||
|
||||
def reverse(self):
|
||||
"""Used to autogenerate the downgrade function."""
|
||||
return DropGeospatialIndexOp(
|
||||
self.index_name,
|
||||
self.table_name,
|
||||
column_name=self.columns[0].name,
|
||||
schema=self.schema,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def batch_create_geospatial_index(
|
||||
cls,
|
||||
operations,
|
||||
index_name,
|
||||
columns,
|
||||
**kw,
|
||||
):
|
||||
"""Issue a "create index" instruction using the current batch migration context."""
|
||||
op = cls(
|
||||
index_name,
|
||||
operations.impl.table_name,
|
||||
columns,
|
||||
schema=operations.impl.schema,
|
||||
**kw,
|
||||
)
|
||||
return operations.invoke(op)
|
||||
|
||||
|
||||
@Operations.register_operation("drop_geospatial_index")
|
||||
@BatchOperations.register_operation("drop_geospatial_index", "batch_drop_geospatial_index")
|
||||
class DropGeospatialIndexOp(ops.DropIndexOp):
|
||||
def __init__(self, *args, column_name, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.column_name = column_name
|
||||
|
||||
@classmethod
|
||||
def drop_geospatial_index(
|
||||
cls,
|
||||
operations,
|
||||
index_name,
|
||||
table_name,
|
||||
column_name,
|
||||
schema=None,
|
||||
unique=False,
|
||||
**kw,
|
||||
):
|
||||
"""Handle the different situations arising from dropping geospatial index from a DB."""
|
||||
op = cls(
|
||||
index_name,
|
||||
table_name=table_name,
|
||||
column_name=column_name,
|
||||
schema=schema,
|
||||
unique=unique,
|
||||
**kw,
|
||||
)
|
||||
return operations.invoke(op)
|
||||
|
||||
def reverse(self):
|
||||
"""Used to autogenerate the downgrade function."""
|
||||
return CreateGeospatialIndexOp(
|
||||
self.index_name,
|
||||
self.table_name,
|
||||
column_name=self.column_name,
|
||||
schema=self.schema,
|
||||
_reverse=self,
|
||||
**self.kw,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_index(cls, index):
|
||||
assert index.table is not None
|
||||
assert len(index.columns) == 1, "A spatial index must be set on one column only"
|
||||
return cls(
|
||||
index.name,
|
||||
index.table.name,
|
||||
column_name=index.columns[0].name,
|
||||
schema=index.table.schema,
|
||||
_reverse=CreateGeospatialIndexOp.from_index(index),
|
||||
**index.kwargs,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def batch_drop_geospatial_index(cls, operations, index_name, **kw):
|
||||
"""Issue a "drop index" instruction using the current batch migration context."""
|
||||
op = cls(
|
||||
index_name,
|
||||
table_name=operations.impl.table_name,
|
||||
schema=operations.impl.schema,
|
||||
**kw,
|
||||
)
|
||||
return operations.invoke(op)
|
||||
|
||||
|
||||
@Operations.implementation_for(CreateGeospatialIndexOp)
|
||||
def create_geospatial_index(operations, operation):
|
||||
"""Handle the actual index creation according to the dialect backend.
|
||||
|
||||
Args:
|
||||
operations: Operations object from alembic base, defining high level migration operations.
|
||||
operation: CreateGeospatialIndexOp call, with attributes for table_name, column_name,
|
||||
column_type, and optional keywords.
|
||||
"""
|
||||
bind = operations.get_bind()
|
||||
dialect = bind.dialect
|
||||
|
||||
if dialect.name == "sqlite":
|
||||
assert len(operation.columns) == 1, "A spatial index must be set on one column only"
|
||||
operations.execute(func.CreateSpatialIndex(operation.table_name, operation.columns[0]))
|
||||
else:
|
||||
idx = operation.to_index(operations.migration_context)
|
||||
operations.impl.create_index(idx)
|
||||
|
||||
|
||||
@Operations.implementation_for(DropGeospatialIndexOp)
|
||||
def drop_geospatial_index(operations, operation):
|
||||
"""Handle the actual index drop according to the dialect backend.
|
||||
|
||||
Args:
|
||||
operations: Operations object from alembic base, defining high level migration operations.
|
||||
operation: DropGeospatialIndexOp call, with attributes for table_name, column_name,
|
||||
column_type, and optional keywords.
|
||||
"""
|
||||
bind = operations.get_bind()
|
||||
dialect = bind.dialect
|
||||
|
||||
if dialect.name == "sqlite":
|
||||
operations.execute(func.DisableSpatialIndex(operation.table_name, operation.column_name))
|
||||
else:
|
||||
operations.impl.drop_index(operation.to_index(operations.migration_context))
|
||||
|
||||
|
||||
@renderers.dispatch_for(CreateGeospatialIndexOp)
|
||||
def render_create_geo_index(autogen_context, op):
|
||||
"""Render the create_geospatial_index operation in migration script."""
|
||||
idx_render = _add_index(autogen_context, op)
|
||||
return idx_render.replace(".create_index(", ".create_geospatial_index(")
|
||||
|
||||
|
||||
@renderers.dispatch_for(DropGeospatialIndexOp)
|
||||
def render_drop_geo_index(autogen_context, op):
|
||||
"""Render the drop_geospatial_index operation in migration script."""
|
||||
idx_render = _drop_index(autogen_context, op)
|
||||
|
||||
# Replace function name
|
||||
text = idx_render.replace(".drop_index(", ".drop_geospatial_index(")
|
||||
|
||||
# Add column name as keyword argument
|
||||
text = text[:-1] + ", column_name='%s')" % (op.column_name,)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
@writer.rewrites(ops.CreateIndexOp)
|
||||
def create_geo_index(context, revision, op):
|
||||
"""Replace the default CreateIndexOp by a geospatial-specific one."""
|
||||
dialect = context.bind.dialect
|
||||
|
||||
if len(op.columns) == 1:
|
||||
col = op.columns[0]
|
||||
if isinstance(col, Column) and _check_spatial_type(
|
||||
col.type, (Geometry, Geography, Raster), dialect
|
||||
):
|
||||
# Fix index properties
|
||||
op.kw["postgresql_using"] = op.kw.get("postgresql_using", "gist")
|
||||
if col.type.use_N_D_index:
|
||||
postgresql_ops = {col.name: "gist_geometry_ops_nd"}
|
||||
else:
|
||||
postgresql_ops = {}
|
||||
op.kw["postgresql_ops"] = op.kw.get("postgresql_ops", postgresql_ops)
|
||||
|
||||
return CreateGeospatialIndexOp(
|
||||
op.index_name,
|
||||
op.table_name,
|
||||
op.columns,
|
||||
schema=op.schema,
|
||||
unique=op.unique,
|
||||
**op.kw,
|
||||
)
|
||||
|
||||
return op
|
||||
|
||||
|
||||
@writer.rewrites(ops.DropIndexOp)
|
||||
def drop_geo_index(context, revision, op):
|
||||
"""Replace the default DropIndexOp by a geospatial-specific one."""
|
||||
dialect = context.bind.dialect
|
||||
idx = op.to_index()
|
||||
|
||||
if len(idx.columns) == 1:
|
||||
col = idx.columns[0]
|
||||
if isinstance(col, Column) and _check_spatial_type(
|
||||
col.type, (Geometry, Geography, Raster), dialect
|
||||
):
|
||||
return DropGeospatialIndexOp(
|
||||
op.index_name,
|
||||
table_name=op.table_name,
|
||||
column_name=col.name,
|
||||
schema=op.schema,
|
||||
**op.kw,
|
||||
)
|
||||
|
||||
return op
|
||||
232
.venv/lib/python3.12/site-packages/geoalchemy2/comparator.py
Normal file
232
.venv/lib/python3.12/site-packages/geoalchemy2/comparator.py
Normal file
@@ -0,0 +1,232 @@
|
||||
"""This module defines a ``Comparator`` class for use with geometry and geography objects.
|
||||
|
||||
This is where spatial operators, like ``&&``, ``&<``, are defined.
|
||||
Spatial operators very often apply to the bounding boxes of geometries. For
|
||||
example, ``geom1 && geom2`` indicates if geom1's bounding box intersects
|
||||
geom2's.
|
||||
|
||||
Examples
|
||||
--------
|
||||
Select the objects whose bounding boxes are to the left of the
|
||||
bounding box of ``POLYGON((-5 45,5 45,5 -45,-5 -45,-5 45))``::
|
||||
|
||||
select([table]).where(table.c.geom.to_left(
|
||||
'POLYGON((-5 45,5 45,5 -45,-5 -45,-5 45))'))
|
||||
|
||||
The ``<<`` and ``>>`` operators are a bit specific, because they have
|
||||
corresponding Python operator (``__lshift__`` and ``__rshift__``). The
|
||||
above ``SELECT`` expression can thus be rewritten like this::
|
||||
|
||||
select([table]).where(
|
||||
table.c.geom << 'POLYGON((-5 45,5 45,5 -45,-5 -45,-5 45))')
|
||||
|
||||
Operators can also be used when using the ORM. For example::
|
||||
|
||||
Session.query(Cls).filter(
|
||||
Cls.geom << 'POLYGON((-5 45,5 45,5 -45,-5 -45,-5 45))')
|
||||
|
||||
Now some other examples with the ``<#>`` operator.
|
||||
|
||||
Select the ten objects that are the closest to ``POINT(0 0)`` (typical
|
||||
closed neighbors problem)::
|
||||
|
||||
select([table]).order_by(table.c.geom.distance_box('POINT(0 0)')).limit(10)
|
||||
|
||||
Using the ORM::
|
||||
|
||||
Session.query(Cls).order_by(Cls.geom.distance_box('POINT(0 0)')).limit(10)
|
||||
"""
|
||||
|
||||
from typing import Union
|
||||
|
||||
from sqlalchemy import types as sqltypes
|
||||
from sqlalchemy.dialects.postgresql import DOUBLE_PRECISION
|
||||
from sqlalchemy.sql.elements import ColumnElement
|
||||
from sqlalchemy.sql.functions import _FunctionGenerator
|
||||
from sqlalchemy.sql.operators import custom_op
|
||||
from sqlalchemy.types import UserDefinedType
|
||||
|
||||
from geoalchemy2.elements import WKBElement
|
||||
from geoalchemy2.elements import WKTElement
|
||||
|
||||
INTERSECTS: custom_op = custom_op("&&")
|
||||
INTERSECTS_ND: custom_op = custom_op("&&&")
|
||||
OVERLAPS_OR_TO_LEFT: custom_op = custom_op("&<")
|
||||
OVERLAPS_OR_TO_RIGHT: custom_op = custom_op("&>")
|
||||
OVERLAPS_OR_BELOW: custom_op = custom_op("&<|")
|
||||
TO_LEFT: custom_op = custom_op("<<")
|
||||
BELOW: custom_op = custom_op("<<|")
|
||||
TO_RIGHT: custom_op = custom_op(">>")
|
||||
CONTAINED: custom_op = custom_op("@")
|
||||
OVERLAPS_OR_ABOVE: custom_op = custom_op("|&>")
|
||||
ABOVE: custom_op = custom_op("|>>")
|
||||
CONTAINS: custom_op = custom_op("~")
|
||||
SAME: custom_op = custom_op("~=")
|
||||
DISTANCE_CENTROID: custom_op = custom_op("<->")
|
||||
DISTANCE_BOX: custom_op = custom_op("<#>")
|
||||
|
||||
_COMPARATOR_INPUT_TYPE = Union[str, WKBElement, WKTElement]
|
||||
|
||||
|
||||
class BaseComparator(UserDefinedType.Comparator):
|
||||
"""A custom comparator base class.
|
||||
|
||||
It adds the ability to call spatial functions on columns that use this kind of comparator.
|
||||
It also defines functions that map to operators supported by ``Geometry``, ``Geography``
|
||||
and ``Raster`` columns.
|
||||
|
||||
This comparator is used by the :class:`geoalchemy2.types.Raster`.
|
||||
"""
|
||||
|
||||
key = None
|
||||
|
||||
def __getattr__(self, name):
|
||||
# Function names that don't start with "ST_" are rejected.
|
||||
# This is not to mess up with SQLAlchemy's use of
|
||||
# hasattr/getattr on Column objects.
|
||||
|
||||
if not name.lower().startswith("st_"):
|
||||
raise AttributeError
|
||||
|
||||
# We create our own _FunctionGenerator here, and use it in place of
|
||||
# SQLAlchemy's "func" object. This is to be able to "bind" the
|
||||
# function to the SQL expression. See also GenericFunction.
|
||||
|
||||
func_ = _FunctionGenerator(expr=self.expr)
|
||||
return getattr(func_, name)
|
||||
|
||||
def intersects(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``&&`` operator. A's BBOX intersects B's."""
|
||||
return self.operate(INTERSECTS, other, result_type=sqltypes.Boolean)
|
||||
|
||||
def overlaps_or_to_left(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``&<`` operator. A's BBOX overlaps or is to the left of B's."""
|
||||
return self.operate(OVERLAPS_OR_TO_LEFT, other, result_type=sqltypes.Boolean)
|
||||
|
||||
def overlaps_or_to_right(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``&>`` operator. A's BBOX overlaps or is to the right of B's."""
|
||||
return self.operate(OVERLAPS_OR_TO_RIGHT, other, result_type=sqltypes.Boolean)
|
||||
|
||||
|
||||
class Comparator(BaseComparator):
|
||||
"""A custom comparator class.
|
||||
|
||||
Used in :class:`geoalchemy2.types.Geometry` and :class:`geoalchemy2.types.Geography`.
|
||||
|
||||
This is where spatial operators like ``<<`` and ``<->`` are defined.
|
||||
"""
|
||||
|
||||
def overlaps_or_below(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``&<|`` operator.
|
||||
|
||||
A's BBOX overlaps or is below B's.
|
||||
"""
|
||||
return self.operate(OVERLAPS_OR_BELOW, other, result_type=sqltypes.Boolean)
|
||||
|
||||
def to_left(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``<<`` operator.
|
||||
|
||||
A's BBOX is strictly to the left of B's.
|
||||
"""
|
||||
return self.operate(TO_LEFT, other, result_type=sqltypes.Boolean)
|
||||
|
||||
def __lshift__(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``<<`` operator.
|
||||
|
||||
A's BBOX is strictly to the left of B's.
|
||||
|
||||
Same as ``to_left``, so::
|
||||
|
||||
table.c.geom << 'POINT(1 2)'
|
||||
|
||||
is the same as::
|
||||
|
||||
table.c.geom.to_left('POINT(1 2)')
|
||||
"""
|
||||
return self.to_left(other)
|
||||
|
||||
def below(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``<<|`` operator.
|
||||
|
||||
A's BBOX is strictly below B's.
|
||||
"""
|
||||
return self.operate(BELOW, other, result_type=sqltypes.Boolean)
|
||||
|
||||
def to_right(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``>>`` operator.
|
||||
|
||||
A's BBOX is strictly to the right of B's.
|
||||
"""
|
||||
return self.operate(TO_RIGHT, other, result_type=sqltypes.Boolean)
|
||||
|
||||
def __rshift__(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``>>`` operator.
|
||||
|
||||
A's BBOX is strictly to the left of B's.
|
||||
|
||||
Same as `to_`right``, so::
|
||||
|
||||
table.c.geom >> 'POINT(1 2)'
|
||||
|
||||
is the same as::
|
||||
|
||||
table.c.geom.to_right('POINT(1 2)')
|
||||
"""
|
||||
return self.to_right(other)
|
||||
|
||||
def contained(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``@`` operator.
|
||||
|
||||
A's BBOX is contained by B's.
|
||||
"""
|
||||
return self.operate(CONTAINED, other, result_type=sqltypes.Boolean)
|
||||
|
||||
def overlaps_or_above(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``|&>`` operator.
|
||||
|
||||
A's BBOX overlaps or is above B's.
|
||||
"""
|
||||
return self.operate(OVERLAPS_OR_ABOVE, other, result_type=sqltypes.Boolean)
|
||||
|
||||
def above(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``|>>`` operator.
|
||||
|
||||
A's BBOX is strictly above B's.
|
||||
"""
|
||||
return self.operate(ABOVE, other, result_type=sqltypes.Boolean)
|
||||
|
||||
def contains(self, other: _COMPARATOR_INPUT_TYPE, **kw) -> ColumnElement:
|
||||
"""The ``~`` operator.
|
||||
|
||||
A's BBOX contains B's.
|
||||
"""
|
||||
return self.operate(CONTAINS, other, result_type=sqltypes.Boolean)
|
||||
|
||||
def same(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``~=`` operator.
|
||||
|
||||
A's BBOX is the same as B's.
|
||||
"""
|
||||
return self.operate(SAME, other, result_type=sqltypes.Boolean)
|
||||
|
||||
def distance_centroid(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``<->`` operator.
|
||||
|
||||
The distance between two points.
|
||||
"""
|
||||
return self.operate(DISTANCE_CENTROID, other, result_type=DOUBLE_PRECISION)
|
||||
|
||||
def distance_box(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``<#>`` operator.
|
||||
|
||||
The distance between bounding box of two geometries.
|
||||
"""
|
||||
return self.operate(DISTANCE_BOX, other, result_type=DOUBLE_PRECISION)
|
||||
|
||||
def intersects_nd(self, other: _COMPARATOR_INPUT_TYPE) -> ColumnElement:
|
||||
"""The ``&&&`` operator.
|
||||
|
||||
This operator returns TRUE if the n-D bounding box of geometry A
|
||||
intersects the n-D bounding box of geometry B.
|
||||
"""
|
||||
return self.operate(INTERSECTS_ND, other, result_type=sqltypes.Boolean)
|
||||
368
.venv/lib/python3.12/site-packages/geoalchemy2/elements.py
Normal file
368
.venv/lib/python3.12/site-packages/geoalchemy2/elements.py
Normal file
@@ -0,0 +1,368 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import binascii
|
||||
import re
|
||||
import struct
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Set
|
||||
from typing import Union
|
||||
|
||||
from sqlalchemy.ext.compiler import compiles
|
||||
from sqlalchemy.sql import functions
|
||||
from sqlalchemy.sql.functions import FunctionElement
|
||||
from sqlalchemy.types import to_instance
|
||||
|
||||
from geoalchemy2.exc import ArgumentError
|
||||
|
||||
BinasciiError = binascii.Error
|
||||
|
||||
function_registry: Set[str] = set()
|
||||
|
||||
|
||||
class _SpatialElement:
|
||||
"""The base class for public spatial elements.
|
||||
|
||||
Args:
|
||||
data: The first argument passed to the constructor is the data wrapped
|
||||
by the ``_SpatialElement`` object being constructed.
|
||||
srid: An integer representing the spatial reference system. E.g. ``4326``.
|
||||
Default value is ``-1``, which means no/unknown reference system.
|
||||
extended: A boolean indicating whether the extended format (EWKT or EWKB)
|
||||
is used. Default is ``None``.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, data, srid: int = -1, extended: Optional[bool] = None) -> None:
|
||||
self.srid = srid
|
||||
self.data = data
|
||||
self.extended = extended
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.desc
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<%s at 0x%x; %s>" % (
|
||||
self.__class__.__name__,
|
||||
id(self),
|
||||
self,
|
||||
) # pragma: no cover
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
try:
|
||||
return (
|
||||
self.extended == other.extended
|
||||
and self.srid == other.srid
|
||||
and self.desc == other.desc
|
||||
)
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
def __ne__(self, other) -> bool:
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.desc, self.srid, self.extended))
|
||||
|
||||
def __getattr__(self, name):
|
||||
#
|
||||
# This is how things like lake.geom.ST_Buffer(2) creates
|
||||
# SQL expressions of this form:
|
||||
#
|
||||
# ST_Buffer(ST_GeomFromWKB(:ST_GeomFromWKB_1), :param_1)
|
||||
#
|
||||
|
||||
# Raise an AttributeError when the attribute name doesn't start
|
||||
# with st_. This is to be nice with other libraries that use
|
||||
# some ducktyping (e.g. hasattr(element, "copy")) to determine
|
||||
# the type of the element.
|
||||
|
||||
if name.lower() not in function_registry:
|
||||
raise AttributeError
|
||||
|
||||
# We create our own _FunctionGenerator here, and use it in place of
|
||||
# SQLAlchemy's "func" object. This is to be able to "bind" the
|
||||
# function to the SQL expression. See also GenericFunction above.
|
||||
func_ = functions._FunctionGenerator(expr=self)
|
||||
return getattr(func_, name)
|
||||
|
||||
def __getstate__(self) -> Dict[str, Any]:
|
||||
state = {
|
||||
"srid": self.srid,
|
||||
"data": str(self),
|
||||
"extended": self.extended,
|
||||
}
|
||||
return state
|
||||
|
||||
def __setstate__(self, state: Dict[str, Any]) -> None:
|
||||
self.srid = state["srid"]
|
||||
self.extended = state["extended"]
|
||||
self.data = self._data_from_desc(state["data"])
|
||||
|
||||
@staticmethod
|
||||
def _data_from_desc(desc):
|
||||
raise NotImplementedError() # pragma: no cover
|
||||
|
||||
|
||||
class WKTElement(_SpatialElement):
|
||||
"""Instances of this class wrap a WKT or EWKT value.
|
||||
|
||||
Usage examples::
|
||||
|
||||
wkt_element_1 = WKTElement('POINT(5 45)')
|
||||
wkt_element_2 = WKTElement('POINT(5 45)', srid=4326)
|
||||
wkt_element_3 = WKTElement('SRID=4326;POINT(5 45)', extended=True)
|
||||
"""
|
||||
|
||||
_REMOVE_SRID = re.compile("(SRID=([0-9]+); ?)?(.*)")
|
||||
SPLIT_WKT_PATTERN = re.compile(r"((SRID=\d+) *; *)?([\w ]+) *(\([-\d\. ,\(\)]+\))")
|
||||
|
||||
geom_from: str = "ST_GeomFromText"
|
||||
geom_from_extended_version: str = "ST_GeomFromEWKT"
|
||||
|
||||
def __init__(self, data: str, srid: int = -1, extended: Optional[bool] = None) -> None:
|
||||
if extended is None:
|
||||
extended = data.startswith("SRID=")
|
||||
if extended and srid == -1:
|
||||
# read srid from EWKT
|
||||
data_s = data.split(";")
|
||||
if len(data_s) != 2:
|
||||
raise ArgumentError("invalid EWKT string {}".format(data))
|
||||
header = data_s[0]
|
||||
try:
|
||||
srid = int(header[5:])
|
||||
except ValueError:
|
||||
raise ArgumentError("invalid EWKT string {}".format(data))
|
||||
_SpatialElement.__init__(self, data, srid, extended)
|
||||
|
||||
@property
|
||||
def desc(self) -> str:
|
||||
"""This element's description string."""
|
||||
return self.data
|
||||
|
||||
@staticmethod
|
||||
def _data_from_desc(desc):
|
||||
return desc
|
||||
|
||||
def as_wkt(self) -> WKTElement:
|
||||
if self.extended:
|
||||
srid_match = self._REMOVE_SRID.match(self.data)
|
||||
assert srid_match is not None
|
||||
return WKTElement(srid_match.group(3), self.srid, extended=False)
|
||||
return WKTElement(self.data, self.srid, self.extended)
|
||||
|
||||
def as_ewkt(self) -> WKTElement:
|
||||
if not self.extended and self.srid != -1:
|
||||
data = f"SRID={self.srid};" + self.data
|
||||
return WKTElement(data, extended=True)
|
||||
return WKTElement(self.data, self.srid, self.extended)
|
||||
|
||||
|
||||
class WKBElement(_SpatialElement):
|
||||
"""Instances of this class wrap a WKB or EWKB value.
|
||||
|
||||
Geometry values read from the database are converted to instances of this
|
||||
type. In most cases you won't need to create ``WKBElement`` instances
|
||||
yourself.
|
||||
|
||||
If ``extended`` is ``True`` and ``srid`` is ``-1`` at construction time
|
||||
then the SRID will be read from the EWKB data.
|
||||
|
||||
Note: you can create ``WKBElement`` objects from Shapely geometries
|
||||
using the :func:`geoalchemy2.shape.from_shape` function.
|
||||
"""
|
||||
|
||||
geom_from: str = "ST_GeomFromWKB"
|
||||
geom_from_extended_version: str = "ST_GeomFromEWKB"
|
||||
|
||||
def __init__(
|
||||
self, data: Union[str, bytes, memoryview], srid: int = -1, extended: Optional[bool] = None
|
||||
) -> None:
|
||||
if srid == -1 or extended is None or extended:
|
||||
# read srid from the EWKB
|
||||
#
|
||||
# WKB struct {
|
||||
# byte byteOrder;
|
||||
# uint32 wkbType;
|
||||
# uint32 SRID;
|
||||
# struct geometry;
|
||||
# }
|
||||
# byteOrder enum {
|
||||
# WKB_XDR = 0, // Most Significant Byte First
|
||||
# WKB_NDR = 1, // Least Significant Byte First
|
||||
# }
|
||||
# See https://trac.osgeo.org/postgis/browser/branches/3.0/doc/ZMSgeoms.txt
|
||||
# for more details about WKB/EWKB specifications.
|
||||
if isinstance(data, str):
|
||||
# SpatiaLite case
|
||||
# assume that the string is an hex value
|
||||
header = binascii.unhexlify(data[:18])
|
||||
else:
|
||||
header = data[:9]
|
||||
byte_order, wkb_type, wkb_srid = header[0], header[1:5], header[5:]
|
||||
byte_order_marker = "<I" if byte_order else ">I"
|
||||
wkb_type_int = (
|
||||
int(struct.unpack(byte_order_marker, wkb_type)[0]) if len(wkb_type) == 4 else 0
|
||||
)
|
||||
if extended is None:
|
||||
if not wkb_type_int:
|
||||
extended = False
|
||||
else:
|
||||
extended = extended or bool(wkb_type_int & 536870912) # Check SRID bit
|
||||
if extended and srid == -1:
|
||||
wkb_srid = struct.unpack(byte_order_marker, wkb_srid)[0]
|
||||
srid = int(wkb_srid)
|
||||
_SpatialElement.__init__(self, data, srid, extended)
|
||||
|
||||
@property
|
||||
def desc(self) -> str:
|
||||
"""This element's description string."""
|
||||
if isinstance(self.data, str):
|
||||
# SpatiaLite case
|
||||
return self.data.lower()
|
||||
desc = str(binascii.hexlify(self.data), encoding="utf-8").lower()
|
||||
return desc
|
||||
|
||||
@staticmethod
|
||||
def _data_from_desc(desc) -> bytes:
|
||||
desc = desc.encode(encoding="utf-8")
|
||||
return binascii.unhexlify(desc)
|
||||
|
||||
def as_wkb(self) -> WKBElement:
|
||||
if self.extended:
|
||||
if isinstance(self.data, str):
|
||||
# SpatiaLite case
|
||||
# assume that the string is an hex value
|
||||
is_hex = True
|
||||
header = binascii.unhexlify(self.data[:10])
|
||||
byte_order, wkb_type = header[0], header[1:5]
|
||||
else:
|
||||
is_hex = False
|
||||
byte_order, wkb_type = self.data[0], self.data[1:5]
|
||||
|
||||
byte_order_marker = "<I" if byte_order else ">I"
|
||||
wkb_type_int = (
|
||||
int(struct.unpack(byte_order_marker, wkb_type)[0]) if len(wkb_type) == 4 else 0
|
||||
)
|
||||
wkb_type_int &= 3758096383 # Set SRID bit to 0 and keep all other bits
|
||||
|
||||
if is_hex:
|
||||
wkb_type_hex = binascii.hexlify(
|
||||
wkb_type_int.to_bytes(4, "little" if byte_order else "big")
|
||||
)
|
||||
data = self.data[:2] + wkb_type_hex.decode("ascii") + self.data[18:]
|
||||
else:
|
||||
buffer = bytearray()
|
||||
buffer.extend(self.data[:1])
|
||||
buffer.extend(struct.pack(byte_order_marker, wkb_type_int))
|
||||
buffer.extend(self.data[9:])
|
||||
data = memoryview(buffer)
|
||||
return WKBElement(data, self.srid, extended=False)
|
||||
return WKBElement(self.data, self.srid)
|
||||
|
||||
def as_ewkb(self) -> WKBElement:
|
||||
if not self.extended and self.srid != -1:
|
||||
if isinstance(self.data, str):
|
||||
# SpatiaLite case
|
||||
# assume that the string is an hex value
|
||||
header = binascii.unhexlify(self.data[:10])
|
||||
byte_order, wkb_type = header[0], header[1:5]
|
||||
else:
|
||||
byte_order, wkb_type = self.data[0], self.data[1:5]
|
||||
byte_order_marker = "<I" if byte_order else ">I"
|
||||
wkb_type_int = int(
|
||||
struct.unpack(byte_order_marker, wkb_type)[0] if len(wkb_type) == 4 else 0
|
||||
)
|
||||
wkb_type_int |= 536870912 # Set SRID bit to 1 and keep all other bits
|
||||
|
||||
data: Union[str, memoryview]
|
||||
if isinstance(self.data, str):
|
||||
wkb_type_hex = binascii.hexlify(
|
||||
wkb_type_int.to_bytes(4, "little" if byte_order else "big")
|
||||
)
|
||||
wkb_srid_hex = binascii.hexlify(
|
||||
self.srid.to_bytes(4, "little" if byte_order else "big")
|
||||
)
|
||||
data = (
|
||||
self.data[:2]
|
||||
+ wkb_type_hex.decode("ascii")
|
||||
+ wkb_srid_hex.decode("ascii")
|
||||
+ self.data[10:]
|
||||
)
|
||||
else:
|
||||
buffer = bytearray()
|
||||
buffer.extend(self.data[:1])
|
||||
buffer.extend(struct.pack(byte_order_marker, wkb_type_int))
|
||||
buffer.extend(struct.pack(byte_order_marker, self.srid))
|
||||
buffer.extend(self.data[5:])
|
||||
data = memoryview(buffer)
|
||||
|
||||
return WKBElement(data, self.srid, extended=True)
|
||||
return WKBElement(self.data, self.srid)
|
||||
|
||||
|
||||
class RasterElement(_SpatialElement):
|
||||
"""Instances of this class wrap a ``raster`` value.
|
||||
|
||||
Raster values read from the database are converted to instances of this type. In
|
||||
most cases you won't need to create ``RasterElement`` instances yourself.
|
||||
"""
|
||||
|
||||
geom_from_extended_version: str = "raster"
|
||||
|
||||
def __init__(self, data: Union[str, bytes, memoryview]) -> None:
|
||||
# read srid from the WKB (binary or hexadecimal format)
|
||||
# The WKB structure is documented in the file
|
||||
# raster/doc/RFC2-WellKnownBinaryFormat of the PostGIS sources.
|
||||
bin_data: Union[str, bytes, memoryview]
|
||||
try:
|
||||
bin_data = binascii.unhexlify(data[:114])
|
||||
except BinasciiError:
|
||||
bin_data = data
|
||||
data = str(binascii.hexlify(data).decode(encoding="utf-8")) # type: ignore
|
||||
byte_order = bin_data[0]
|
||||
srid = bin_data[53:57]
|
||||
srid = struct.unpack("<I" if byte_order else ">I", srid)[0] # type: ignore
|
||||
_SpatialElement.__init__(self, data, int(srid), True)
|
||||
|
||||
@property
|
||||
def desc(self) -> str:
|
||||
"""This element's description string."""
|
||||
return self.data
|
||||
|
||||
@staticmethod
|
||||
def _data_from_desc(desc):
|
||||
return desc
|
||||
|
||||
|
||||
class CompositeElement(FunctionElement):
|
||||
"""Instances of this class wrap a Postgres composite type."""
|
||||
|
||||
inherit_cache: bool = False
|
||||
"""The cache is disabled for this class."""
|
||||
|
||||
def __init__(self, base, field, type_) -> None:
|
||||
self.name = field
|
||||
self.type = to_instance(type_)
|
||||
|
||||
super(CompositeElement, self).__init__(base)
|
||||
|
||||
|
||||
@compiles(CompositeElement)
|
||||
def _compile_pgelem(expr, compiler, **kw) -> str:
|
||||
return "(%s).%s" % (compiler.process(expr.clauses, **kw), expr.name)
|
||||
|
||||
|
||||
__all__: List[str] = [
|
||||
"_SpatialElement",
|
||||
"CompositeElement",
|
||||
"RasterElement",
|
||||
"WKBElement",
|
||||
"WKTElement",
|
||||
]
|
||||
|
||||
|
||||
def __dir__() -> List[str]:
|
||||
return __all__
|
||||
9
.venv/lib/python3.12/site-packages/geoalchemy2/exc.py
Normal file
9
.venv/lib/python3.12/site-packages/geoalchemy2/exc.py
Normal file
@@ -0,0 +1,9 @@
|
||||
"""Exceptions used with GeoAlchemy2."""
|
||||
|
||||
|
||||
class GeoAlchemyError(Exception):
|
||||
"""Generic error class."""
|
||||
|
||||
|
||||
class ArgumentError(GeoAlchemyError):
|
||||
"""Raised when an invalid or conflicting function argument is supplied."""
|
||||
288
.venv/lib/python3.12/site-packages/geoalchemy2/functions.py
Normal file
288
.venv/lib/python3.12/site-packages/geoalchemy2/functions.py
Normal file
@@ -0,0 +1,288 @@
|
||||
"""This module defines the internals to map the spatial functions to the spatial columns.
|
||||
|
||||
This module defines the :class:`GenericFunction` class, which is the base for
|
||||
the implementation of spatial functions in GeoAlchemy. This module is also
|
||||
where actual spatial functions are defined. Spatial functions supported by
|
||||
GeoAlchemy are defined in this module. See :class:`GenericFunction` to know how
|
||||
to create new spatial functions.
|
||||
|
||||
.. note::
|
||||
|
||||
By convention the names of spatial functions are prefixed by ``ST_``. This
|
||||
is to be consistent with PostGIS', which itself is based on the ``SQL-MM``
|
||||
standard.
|
||||
|
||||
Functions created by subclassing :class:`GenericFunction` can be called
|
||||
in several ways:
|
||||
|
||||
* By using the ``func`` object, which is the SQLAlchemy standard way of calling
|
||||
a function. For example, without the ORM::
|
||||
|
||||
select([func.ST_Area(lake_table.c.geom)])
|
||||
|
||||
and with the ORM::
|
||||
|
||||
Session.query(func.ST_Area(Lake.geom))
|
||||
|
||||
* By applying the function to a geometry column. For example, without the
|
||||
ORM::
|
||||
|
||||
select([lake_table.c.geom.ST_Area()])
|
||||
|
||||
and with the ORM::
|
||||
|
||||
Session.query(Lake.geom.ST_Area())
|
||||
|
||||
* By applying the function to a :class:`geoalchemy2.elements.WKBElement`
|
||||
object (:class:`geoalchemy2.elements.WKBElement` is the type into
|
||||
which GeoAlchemy converts geometry values read from the database), or
|
||||
to a :class:`geoalchemy2.elements.WKTElement` object. For example,
|
||||
without the ORM::
|
||||
|
||||
conn.scalar(lake['geom'].ST_Area())
|
||||
|
||||
and with the ORM::
|
||||
|
||||
session.scalar(lake.geom.ST_Area())
|
||||
|
||||
.. warning::
|
||||
|
||||
A few functions (like `ST_Transform()`, `ST_Union()`, `ST_SnapToGrid()`, ...) can be used on
|
||||
several spatial types (:class:`geoalchemy2.types.Geometry`,
|
||||
:class:`geoalchemy2.types.Geography` and / or :class:`geoalchemy2.types.Raster` types). In
|
||||
GeoAlchemy2, these functions are only defined for the :class:`geoalchemy2.types.Geometry` type,
|
||||
as it can not be defined for several types at the same time. Therefore, using these functions on
|
||||
:class:`geoalchemy2.types.Geography` or :class:`geoalchemy2.types.Raster` requires minor
|
||||
tweaking to enforce the type by passing the `type_=Geography` or `type_=Raster` argument to the
|
||||
function::
|
||||
|
||||
s = select([func.ST_Transform(
|
||||
lake_table.c.raster,
|
||||
2154,
|
||||
type_=Raster)
|
||||
.label('transformed_raster')])
|
||||
|
||||
Reference
|
||||
---------
|
||||
|
||||
"""
|
||||
|
||||
import re
|
||||
from typing import List
|
||||
from typing import Type
|
||||
|
||||
from sqlalchemy import inspect
|
||||
from sqlalchemy.ext.compiler import compiles
|
||||
from sqlalchemy.sql import annotation
|
||||
from sqlalchemy.sql import functions
|
||||
from sqlalchemy.sql.elements import ColumnElement
|
||||
|
||||
from geoalchemy2 import elements
|
||||
from geoalchemy2._functions import _FUNCTIONS
|
||||
from geoalchemy2._functions_helpers import _get_docstring
|
||||
|
||||
_GeoFunctionBase: Type[functions.GenericFunction]
|
||||
_GeoFunctionParent: Type[functions.GenericFunction]
|
||||
try:
|
||||
# SQLAlchemy < 2
|
||||
|
||||
from sqlalchemy.sql.functions import _GenericMeta # type: ignore
|
||||
from sqlalchemy.util import with_metaclass # type: ignore
|
||||
|
||||
class _GeoGenericMeta(_GenericMeta):
|
||||
"""Extend the registering mechanism of sqlalchemy.
|
||||
|
||||
The spatial functions are registered in a specific registry for geoalchemy2.
|
||||
"""
|
||||
|
||||
_register = False
|
||||
|
||||
def __init__(cls, clsname, bases, clsdict) -> None:
|
||||
# Register the function
|
||||
elements.function_registry.add(clsname.lower())
|
||||
|
||||
super(_GeoGenericMeta, cls).__init__(clsname, bases, clsdict)
|
||||
|
||||
_GeoFunctionBase = with_metaclass(_GeoGenericMeta, functions.GenericFunction)
|
||||
_GeoFunctionParent = functions.GenericFunction
|
||||
except ImportError:
|
||||
# SQLAlchemy >= 2
|
||||
|
||||
class GeoGenericFunction(functions.GenericFunction):
|
||||
def __init_subclass__(cls) -> None:
|
||||
if annotation.Annotated not in cls.__mro__:
|
||||
cls._register_geo_function(cls.__name__, cls.__dict__)
|
||||
super().__init_subclass__()
|
||||
|
||||
@classmethod
|
||||
def _register_geo_function(cls, clsname, clsdict) -> None:
|
||||
# Check _register attribute status
|
||||
cls._register = getattr(cls, "_register", True)
|
||||
|
||||
# Register the function if required
|
||||
if cls._register:
|
||||
elements.function_registry.add(clsname.lower())
|
||||
else:
|
||||
# Set _register to True to register child classes by default
|
||||
cls._register = True
|
||||
|
||||
_GeoFunctionBase = GeoGenericFunction
|
||||
_GeoFunctionParent = GeoGenericFunction
|
||||
|
||||
|
||||
class TableRowElement(ColumnElement):
|
||||
inherit_cache: bool = False
|
||||
"""The cache is disabled for this class."""
|
||||
|
||||
def __init__(self, selectable: bool) -> None:
|
||||
self.selectable = selectable
|
||||
|
||||
@property
|
||||
def _from_objects(self) -> List[bool]:
|
||||
return [self.selectable]
|
||||
|
||||
|
||||
class ST_AsGeoJSON(_GeoFunctionBase): # type: ignore
|
||||
"""Special process for the ST_AsGeoJSON() function.
|
||||
|
||||
This is to be able to work with its feature version introduced in PostGIS 3.
|
||||
"""
|
||||
|
||||
name: str = "ST_AsGeoJSON"
|
||||
inherit_cache: bool = True
|
||||
"""The cache is enabled for this class."""
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
expr = kwargs.pop("expr", None)
|
||||
args_list = list(args)
|
||||
if expr is not None:
|
||||
args_list = [expr] + args_list
|
||||
for idx, element in enumerate(args_list):
|
||||
if isinstance(element, functions.Function):
|
||||
continue
|
||||
elif isinstance(element, elements._SpatialElement):
|
||||
if element.extended:
|
||||
func_name = element.geom_from_extended_version
|
||||
func_args = [element.data]
|
||||
else:
|
||||
func_name = element.geom_from
|
||||
func_args = [element.data, element.srid]
|
||||
args_list[idx] = getattr(functions.func, func_name)(*func_args)
|
||||
else:
|
||||
try:
|
||||
insp = inspect(element)
|
||||
if hasattr(insp, "selectable"):
|
||||
args_list[idx] = TableRowElement(insp.selectable)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
_GeoFunctionParent.__init__(self, *args_list, **kwargs)
|
||||
|
||||
__doc__ = (
|
||||
'Return the geometry as a GeoJSON "geometry" object, or the row as a '
|
||||
'GeoJSON feature" object (PostGIS 3 only). (Cf GeoJSON specifications RFC '
|
||||
"7946). 2D and 3D Geometries are both supported. GeoJSON only support SFS "
|
||||
"1.1 geometry types (no curve support for example). "
|
||||
"See https://postgis.net/docs/ST_AsGeoJSON.html"
|
||||
)
|
||||
|
||||
|
||||
@compiles(TableRowElement)
|
||||
def _compile_table_row_thing(element, compiler, **kw):
|
||||
# In order to get a name as reliably as possible, noting that some
|
||||
# SQL compilers don't say "table AS name" and might not have the "AS",
|
||||
# table and alias names can have spaces in them, etc., get it from
|
||||
# a column instead because that's what we want to be showing here anyway.
|
||||
|
||||
compiled = compiler.process(list(element.selectable.columns)[0], **kw)
|
||||
|
||||
# 1. check for exact name of the selectable is here, use that.
|
||||
# This way if it has dots and spaces and anything else in it, we
|
||||
# can get it w/ correct quoting
|
||||
schema = getattr(element.selectable, "schema", "")
|
||||
name = element.selectable.name
|
||||
pattern = r"(.?%s.?\.)?(.?%s.?)\." % (schema, name)
|
||||
m = re.match(pattern, compiled)
|
||||
if m:
|
||||
return m.group(2)
|
||||
|
||||
# 2. just split on the dot, assume anonymized name
|
||||
return compiled.split(".")[0]
|
||||
|
||||
|
||||
class GenericFunction(_GeoFunctionBase): # type: ignore
|
||||
"""The base class for GeoAlchemy functions.
|
||||
|
||||
This class inherits from ``sqlalchemy.sql.functions.GenericFunction``, so
|
||||
functions defined by subclassing this class can be given a fixed return
|
||||
type. For example, functions like :class:`ST_Buffer` and
|
||||
:class:`ST_Envelope` have their ``type`` attributes set to
|
||||
:class:`geoalchemy2.types.Geometry`.
|
||||
|
||||
This class allows constructs like ``Lake.geom.ST_Buffer(2)``. In that
|
||||
case the ``Function`` instance is bound to an expression (``Lake.geom``
|
||||
here), and that expression is passed to the function when the function
|
||||
is actually called.
|
||||
|
||||
If you need to use a function that GeoAlchemy does not provide you will
|
||||
certainly want to subclass this class. For example, if you need the
|
||||
``ST_TransScale`` spatial function, which isn't (currently) natively
|
||||
supported by GeoAlchemy, you will write this::
|
||||
|
||||
from geoalchemy2 import Geometry
|
||||
from geoalchemy2.functions import GenericFunction
|
||||
|
||||
class ST_TransScale(GenericFunction):
|
||||
name = 'ST_TransScale'
|
||||
type = Geometry
|
||||
"""
|
||||
|
||||
# Set _register to False in order not to register this class in
|
||||
# sqlalchemy.sql.functions._registry. Only its children will be registered.
|
||||
_register = False
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
expr = kwargs.pop("expr", None)
|
||||
args_list = list(args)
|
||||
if expr is not None:
|
||||
args_list = [expr] + args_list
|
||||
for idx, elem in enumerate(args_list):
|
||||
if isinstance(elem, elements._SpatialElement):
|
||||
if elem.extended:
|
||||
func_name = elem.geom_from_extended_version
|
||||
func_args = [elem.data]
|
||||
else:
|
||||
func_name = elem.geom_from
|
||||
func_args = [elem.data, elem.srid]
|
||||
args_list[idx] = getattr(functions.func, func_name)(*func_args)
|
||||
_GeoFunctionParent.__init__(self, *args_list, **kwargs)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"GenericFunction",
|
||||
"ST_AsGeoJSON",
|
||||
"TableRowElement",
|
||||
]
|
||||
|
||||
|
||||
def _create_dynamic_functions() -> None:
|
||||
# Iterate through _FUNCTIONS and create GenericFunction classes dynamically
|
||||
for name, type_, doc in _FUNCTIONS:
|
||||
attributes = {
|
||||
"name": name,
|
||||
"inherit_cache": True,
|
||||
"__doc__": _get_docstring(name, doc, type_),
|
||||
}
|
||||
|
||||
if type_ is not None:
|
||||
attributes["type"] = type_
|
||||
|
||||
globals()[name] = type(name, (GenericFunction,), attributes)
|
||||
__all__.append(name)
|
||||
|
||||
|
||||
_create_dynamic_functions()
|
||||
|
||||
|
||||
def __dir__() -> List[str]:
|
||||
return __all__
|
||||
3913
.venv/lib/python3.12/site-packages/geoalchemy2/functions.pyi
Normal file
3913
.venv/lib/python3.12/site-packages/geoalchemy2/functions.pyi
Normal file
File diff suppressed because it is too large
Load Diff
97
.venv/lib/python3.12/site-packages/geoalchemy2/shape.py
Normal file
97
.venv/lib/python3.12/site-packages/geoalchemy2/shape.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""This module provides utility functions for integrating with Shapely.
|
||||
|
||||
.. note::
|
||||
|
||||
As GeoAlchemy 2 itself has no dependency on `Shapely`, applications using
|
||||
functions of this module have to ensure that `Shapely` is available.
|
||||
"""
|
||||
|
||||
from contextlib import contextmanager
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Union
|
||||
|
||||
try:
|
||||
import shapely.wkb
|
||||
import shapely.wkt
|
||||
from shapely.wkb import dumps
|
||||
|
||||
HAS_SHAPELY = True
|
||||
_shapely_exc = None
|
||||
except ImportError as exc:
|
||||
HAS_SHAPELY = False
|
||||
_shapely_exc = exc
|
||||
|
||||
from geoalchemy2.elements import WKBElement
|
||||
from geoalchemy2.elements import WKTElement
|
||||
|
||||
|
||||
@contextmanager
|
||||
def check_shapely():
|
||||
if not HAS_SHAPELY:
|
||||
raise ImportError(
|
||||
"This feature needs the optional Shapely dependency. "
|
||||
"Please install it with 'pip install geoalchemy2[shapely]'."
|
||||
) from _shapely_exc
|
||||
yield
|
||||
|
||||
|
||||
@check_shapely()
|
||||
def to_shape(element: Union[WKBElement, WKTElement]):
|
||||
"""Function to convert a :class:`geoalchemy2.types.SpatialElement` to a Shapely geometry.
|
||||
|
||||
Args:
|
||||
element: The element to convert into a ``Shapely`` object.
|
||||
|
||||
Example::
|
||||
|
||||
lake = Session.query(Lake).get(1)
|
||||
polygon = to_shape(lake.geom)
|
||||
"""
|
||||
assert isinstance(element, (WKBElement, WKTElement))
|
||||
if isinstance(element, WKBElement):
|
||||
data, hex = (
|
||||
(element.data, True) if isinstance(element.data, str) else (bytes(element.data), False)
|
||||
)
|
||||
return shapely.wkb.loads(data, hex=hex)
|
||||
elif isinstance(element, WKTElement):
|
||||
if element.extended:
|
||||
return shapely.wkt.loads(element.data.split(";", 1)[1])
|
||||
else:
|
||||
return shapely.wkt.loads(element.data)
|
||||
else:
|
||||
raise TypeError("Only WKBElement and WKTElement objects are supported")
|
||||
|
||||
|
||||
@check_shapely()
|
||||
def from_shape(shape, srid: int = -1, extended: Optional[bool] = False) -> WKBElement:
|
||||
"""Function to convert a Shapely geometry to a :class:`geoalchemy2.types.WKBElement`.
|
||||
|
||||
Args:
|
||||
shape: The shape to convert.
|
||||
srid: An integer representing the spatial reference system. E.g. ``4326``.
|
||||
Default value is ``-1``, which means no/unknown reference system.
|
||||
extended: A boolean to switch between WKB and EWKB.
|
||||
Default value is False.
|
||||
|
||||
Example::
|
||||
|
||||
from shapely.geometry import Point
|
||||
wkb_element = from_shape(Point(5, 45), srid=4326)
|
||||
ewkb_element = from_shape(Point(5, 45), srid=4326, extended=True)
|
||||
"""
|
||||
return WKBElement(
|
||||
memoryview(dumps(shape, srid=srid if extended else None)),
|
||||
srid=srid,
|
||||
extended=extended,
|
||||
)
|
||||
|
||||
|
||||
__all__: List[str] = [
|
||||
"from_shape",
|
||||
"to_shape",
|
||||
]
|
||||
|
||||
|
||||
def __dir__() -> List[str]:
|
||||
return __all__
|
||||
433
.venv/lib/python3.12/site-packages/geoalchemy2/types/__init__.py
Normal file
433
.venv/lib/python3.12/site-packages/geoalchemy2/types/__init__.py
Normal file
@@ -0,0 +1,433 @@
|
||||
"""This module defines the Column types.
|
||||
|
||||
The :class:`geoalchemy2.types.Geometry`, :class:`geoalchemy2.types.Geography`, and
|
||||
:class:`geoalchemy2.types.Raster` classes are used when defining geometry, geography and raster
|
||||
columns/properties in models.
|
||||
"""
|
||||
|
||||
import warnings
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy.dialects import postgresql
|
||||
from sqlalchemy.dialects.postgresql.base import ischema_names as _postgresql_ischema_names
|
||||
from sqlalchemy.dialects.sqlite.base import ischema_names as _sqlite_ischema_names
|
||||
from sqlalchemy.ext.compiler import compiles
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.types import Float
|
||||
from sqlalchemy.types import Integer
|
||||
from sqlalchemy.types import UserDefinedType
|
||||
|
||||
try:
|
||||
# SQLAlchemy >= 2
|
||||
from sqlalchemy.sql._typing import _TypeEngineArgument
|
||||
except ImportError:
|
||||
# SQLAlchemy < 2
|
||||
_TypeEngineArgument = Any # type: ignore
|
||||
|
||||
from geoalchemy2.comparator import BaseComparator
|
||||
from geoalchemy2.comparator import Comparator
|
||||
from geoalchemy2.elements import CompositeElement
|
||||
from geoalchemy2.elements import RasterElement
|
||||
from geoalchemy2.elements import WKBElement
|
||||
from geoalchemy2.exc import ArgumentError
|
||||
from geoalchemy2.types import dialects
|
||||
|
||||
|
||||
def select_dialect(dialect_name):
|
||||
"""Select the dialect from its name."""
|
||||
known_dialects = {
|
||||
"geopackage": dialects.geopackage,
|
||||
"mysql": dialects.mysql,
|
||||
"mariadb": dialects.mysql,
|
||||
"postgresql": dialects.postgresql,
|
||||
"sqlite": dialects.sqlite,
|
||||
}
|
||||
return known_dialects.get(dialect_name, dialects.common)
|
||||
|
||||
|
||||
class _GISType(UserDefinedType):
|
||||
"""The base class for spatial types.
|
||||
|
||||
This class defines ``bind_expression`` and ``column_expression`` methods
|
||||
that wrap column expressions in ``ST_GeomFromEWKT``, ``ST_GeogFromText``,
|
||||
or ``ST_AsEWKB`` calls.
|
||||
|
||||
This class also defines ``result_processor`` and ``bind_processor``
|
||||
methods. The function returned by ``result_processor`` converts WKB values
|
||||
received from the database to :class:`geoalchemy2.elements.WKBElement`
|
||||
objects. The function returned by ``bind_processor`` converts
|
||||
:class:`geoalchemy2.elements.WKTElement` objects to EWKT strings.
|
||||
|
||||
Args:
|
||||
geometry_type: The geometry type.
|
||||
|
||||
Possible values are:
|
||||
|
||||
* ``"GEOMETRY"``,
|
||||
* ``"POINT"``,
|
||||
* ``"LINESTRING"``,
|
||||
* ``"POLYGON"``,
|
||||
* ``"MULTIPOINT"``,
|
||||
* ``"MULTILINESTRING"``,
|
||||
* ``"MULTIPOLYGON"``,
|
||||
* ``"GEOMETRYCOLLECTION"``,
|
||||
* ``"CURVE"``,
|
||||
* ``None``.
|
||||
|
||||
The latter is actually not supported with
|
||||
:class:`geoalchemy2.types.Geography`.
|
||||
|
||||
When set to ``None`` then no "geometry type" constraints will be
|
||||
attached to the geometry type declaration.
|
||||
|
||||
Default is ``"GEOMETRY"``.
|
||||
|
||||
srid: The SRID for this column. E.g. 4326. Default is ``-1``.
|
||||
dimension: The dimension of the geometry. Default is ``2``.
|
||||
spatial_index: Indicate if a spatial index should be created. Default is ``True``.
|
||||
use_N_D_index: Use the N-D index instead of the standard 2-D index.
|
||||
use_typmod: By default PostgreSQL type modifiers are used to create the geometry
|
||||
column. To use check constraints instead set ``use_typmod`` to
|
||||
``False``. By default this option is not included in the call to
|
||||
``AddGeometryColumn``. Note that this option is only available for PostGIS 2.x.
|
||||
"""
|
||||
|
||||
name: Optional[str] = None
|
||||
""" Name used for defining the main geo type (geometry or geography)
|
||||
in CREATE TABLE statements. Set in subclasses. """
|
||||
|
||||
from_text: Optional[str] = None
|
||||
""" The name of "from text" function for this type.
|
||||
Set in subclasses. """
|
||||
|
||||
as_binary: Optional[str] = None
|
||||
""" The name of the "as binary" function for this type.
|
||||
Set in subclasses. """
|
||||
|
||||
comparator_factory: Any = Comparator
|
||||
""" This is the way by which spatial operators are defined for
|
||||
geometry/geography columns. """
|
||||
|
||||
cache_ok = False
|
||||
""" Disable cache for this type. """
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
geometry_type: Optional[str] = "GEOMETRY",
|
||||
srid=-1,
|
||||
dimension=2,
|
||||
spatial_index=True,
|
||||
use_N_D_index=False,
|
||||
use_typmod: Optional[bool] = None,
|
||||
from_text: Optional[str] = None,
|
||||
name: Optional[str] = None,
|
||||
nullable=True,
|
||||
_spatial_index_reflected=None,
|
||||
) -> None:
|
||||
geometry_type, srid = self.check_ctor_args(
|
||||
geometry_type, srid, dimension, use_typmod, nullable
|
||||
)
|
||||
self.geometry_type = geometry_type
|
||||
self.srid = srid
|
||||
if name is not None:
|
||||
self.name = name
|
||||
if from_text is not None:
|
||||
self.from_text = from_text
|
||||
self.dimension = dimension
|
||||
self.spatial_index = spatial_index
|
||||
self.use_N_D_index = use_N_D_index
|
||||
self.use_typmod = use_typmod
|
||||
self.extended: Optional[bool] = self.as_binary == "ST_AsEWKB"
|
||||
self.nullable = nullable
|
||||
self._spatial_index_reflected = _spatial_index_reflected
|
||||
|
||||
def get_col_spec(self):
|
||||
if not self.geometry_type:
|
||||
return self.name
|
||||
return "%s(%s,%d)" % (self.name, self.geometry_type, self.srid)
|
||||
|
||||
def column_expression(self, col):
|
||||
"""Specific column_expression that automatically adds a conversion function."""
|
||||
return getattr(func, self.as_binary)(col, type_=self)
|
||||
|
||||
def result_processor(self, dialect, coltype):
|
||||
"""Specific result_processor that automatically process spatial elements."""
|
||||
|
||||
def process(value):
|
||||
if value is not None:
|
||||
kwargs = {}
|
||||
if self.srid > 0:
|
||||
kwargs["srid"] = self.srid
|
||||
if self.extended is not None and dialect.name not in ["mysql", "mariadb"]:
|
||||
kwargs["extended"] = self.extended
|
||||
return self.ElementType(value, **kwargs)
|
||||
|
||||
return process
|
||||
|
||||
def bind_expression(self, bindvalue):
|
||||
"""Specific bind_expression that automatically adds a conversion function."""
|
||||
return getattr(func, self.from_text)(bindvalue, type_=self)
|
||||
|
||||
def bind_processor(self, dialect):
|
||||
"""Specific bind_processor that automatically process spatial elements."""
|
||||
|
||||
def process(bindvalue):
|
||||
return select_dialect(dialect.name).bind_processor_process(self, bindvalue)
|
||||
|
||||
return process
|
||||
|
||||
@staticmethod
|
||||
def check_ctor_args(geometry_type, srid, dimension, use_typmod, nullable):
|
||||
try:
|
||||
# passing default SRID if it is NULL from DB
|
||||
srid = int(srid if srid is not None else -1)
|
||||
except (ValueError, TypeError):
|
||||
raise ArgumentError("srid must be convertible to an integer")
|
||||
if geometry_type:
|
||||
geometry_type = geometry_type.upper()
|
||||
elif srid > 0:
|
||||
warnings.warn("srid not enforced when geometry_type is None")
|
||||
|
||||
if use_typmod is not None and not nullable:
|
||||
raise ArgumentError(
|
||||
'The "nullable" and "use_typmod" arguments can not be used together'
|
||||
)
|
||||
|
||||
return geometry_type, srid
|
||||
|
||||
|
||||
@compiles(_GISType, "mariadb")
|
||||
@compiles(_GISType, "mysql")
|
||||
def get_col_spec(self, *args, **kwargs):
|
||||
if self.geometry_type is not None:
|
||||
spec = "%s" % self.geometry_type
|
||||
else:
|
||||
spec = "GEOMETRY"
|
||||
|
||||
if not self.nullable or self.spatial_index:
|
||||
spec += " NOT NULL"
|
||||
if self.srid > 0:
|
||||
spec += " SRID %d" % self.srid
|
||||
return spec
|
||||
|
||||
|
||||
class Geometry(_GISType):
|
||||
"""The Geometry type.
|
||||
|
||||
Creating a geometry column is done like this::
|
||||
|
||||
Column(Geometry(geometry_type='POINT', srid=4326))
|
||||
|
||||
See :class:`geoalchemy2.types._GISType` for the list of arguments that can
|
||||
be passed to the constructor.
|
||||
|
||||
If ``srid`` is set then the ``WKBElement`` objects resulting from queries will
|
||||
have that SRID, and, when constructing the ``WKBElement`` objects, the SRID
|
||||
won't be read from the data returned by the database. If ``srid`` is not set
|
||||
(meaning it's ``-1``) then the SRID set in ``WKBElement`` objects will be read
|
||||
from the data returned by the database.
|
||||
"""
|
||||
|
||||
name = "geometry"
|
||||
""" Type name used for defining geometry columns in ``CREATE TABLE``. """
|
||||
|
||||
from_text = "ST_GeomFromEWKT"
|
||||
""" The "from text" geometry constructor. Used by the parent class'
|
||||
``bind_expression`` method. """
|
||||
|
||||
as_binary = "ST_AsEWKB"
|
||||
""" The "as binary" function to use. Used by the parent class'
|
||||
``column_expression`` method. """
|
||||
|
||||
ElementType = WKBElement
|
||||
""" The element class to use. Used by the parent class'
|
||||
``result_processor`` method. """
|
||||
|
||||
cache_ok = False
|
||||
""" Disable cache for this type. """
|
||||
|
||||
|
||||
class Geography(_GISType):
|
||||
"""The Geography type.
|
||||
|
||||
Creating a geography column is done like this::
|
||||
|
||||
Column(Geography(geometry_type='POINT', srid=4326))
|
||||
|
||||
See :class:`geoalchemy2.types._GISType` for the list of arguments that can
|
||||
be passed to the constructor.
|
||||
"""
|
||||
|
||||
name = "geography"
|
||||
""" Type name used for defining geography columns in ``CREATE TABLE``. """
|
||||
|
||||
from_text = "ST_GeogFromText"
|
||||
""" The ``FromText`` geography constructor. Used by the parent class'
|
||||
``bind_expression`` method. """
|
||||
|
||||
as_binary = "ST_AsBinary"
|
||||
""" The "as binary" function to use. Used by the parent class'
|
||||
``column_expression`` method. """
|
||||
|
||||
ElementType = WKBElement
|
||||
""" The element class to use. Used by the parent class'
|
||||
``result_processor`` method. """
|
||||
|
||||
cache_ok = False
|
||||
""" Disable cache for this type. """
|
||||
|
||||
|
||||
class Raster(_GISType):
|
||||
"""The Raster column type.
|
||||
|
||||
Creating a raster column is done like this::
|
||||
|
||||
Column(Raster)
|
||||
|
||||
This class defines the ``result_processor`` method, so that raster values
|
||||
received from the database are converted to
|
||||
:class:`geoalchemy2.elements.RasterElement` objects.
|
||||
|
||||
Args:
|
||||
spatial_index: Indicate if a spatial index should be created. Default is ``True``.
|
||||
"""
|
||||
|
||||
comparator_factory = BaseComparator
|
||||
"""
|
||||
This is the way by which spatial operators and functions are
|
||||
defined for raster columns.
|
||||
"""
|
||||
|
||||
name = "raster"
|
||||
""" Type name used for defining raster columns in ``CREATE TABLE``. """
|
||||
|
||||
from_text = "raster"
|
||||
""" The "from text" raster constructor. Used by the parent class'
|
||||
``bind_expression`` method. """
|
||||
|
||||
as_binary = "raster"
|
||||
""" The "as binary" function to use. Used by the parent class'
|
||||
``column_expression`` method. """
|
||||
|
||||
ElementType = RasterElement
|
||||
""" The element class to use. Used by the parent class'
|
||||
``result_processor`` method. """
|
||||
|
||||
cache_ok = False
|
||||
""" Disable cache for this type. """
|
||||
|
||||
def __init__(self, spatial_index=True, from_text=None, name=None, nullable=True) -> None:
|
||||
# Enforce default values
|
||||
super(Raster, self).__init__(
|
||||
geometry_type=None,
|
||||
srid=-1,
|
||||
dimension=2,
|
||||
spatial_index=spatial_index,
|
||||
use_N_D_index=False,
|
||||
use_typmod=False,
|
||||
from_text=from_text,
|
||||
name=name,
|
||||
nullable=nullable,
|
||||
)
|
||||
self.extended = None
|
||||
|
||||
@staticmethod
|
||||
def check_ctor_args(*args, **kwargs):
|
||||
return None, -1
|
||||
|
||||
|
||||
class _DummyGeometry(Geometry):
|
||||
"""A dummy type only used with SQLite."""
|
||||
|
||||
def get_col_spec(self):
|
||||
return self.geometry_type or "GEOMETRY"
|
||||
|
||||
|
||||
class CompositeType(UserDefinedType):
|
||||
"""A composite type used by some spatial functions.
|
||||
|
||||
A wrapper for :class:`geoalchemy2.elements.CompositeElement`, that can be
|
||||
used as the return type in PostgreSQL functions that return composite
|
||||
values.
|
||||
|
||||
This is used as the base class of :class:`geoalchemy2.types.GeometryDump`.
|
||||
"""
|
||||
|
||||
typemap: Dict[str, _TypeEngineArgument] = {}
|
||||
""" Dictionary used for defining the content types and their
|
||||
corresponding keys. Set in subclasses. """
|
||||
|
||||
class comparator_factory(UserDefinedType.Comparator):
|
||||
def __getattr__(self, key):
|
||||
try:
|
||||
type_ = self.type.typemap[key]
|
||||
except KeyError:
|
||||
raise AttributeError("Type '%s' doesn't have an attribute: '%s'" % (self.type, key))
|
||||
|
||||
return CompositeElement(self.expr, key, type_)
|
||||
|
||||
|
||||
class GeometryDump(CompositeType):
|
||||
"""The return type for functions like ``ST_Dump``.
|
||||
|
||||
The type consists in a path and a geom field.
|
||||
You should normally never use this class directly.
|
||||
"""
|
||||
|
||||
typemap = {"path": postgresql.ARRAY(Integer), "geom": Geometry}
|
||||
""" Dictionary defining the contents of a ``geometry_dump``. """
|
||||
|
||||
cache_ok = True
|
||||
""" Enable cache for this type. """
|
||||
|
||||
|
||||
# Register Geometry, Geography and Raster to SQLAlchemy's reflection subsystems.
|
||||
_postgresql_ischema_names["geometry"] = Geometry
|
||||
_postgresql_ischema_names["geography"] = Geography
|
||||
_postgresql_ischema_names["raster"] = Raster
|
||||
|
||||
_sqlite_ischema_names["GEOMETRY"] = Geometry
|
||||
_sqlite_ischema_names["POINT"] = Geometry
|
||||
_sqlite_ischema_names["LINESTRING"] = Geometry
|
||||
_sqlite_ischema_names["POLYGON"] = Geometry
|
||||
_sqlite_ischema_names["MULTIPOINT"] = Geometry
|
||||
_sqlite_ischema_names["MULTILINESTRING"] = Geometry
|
||||
_sqlite_ischema_names["MULTIPOLYGON"] = Geometry
|
||||
_sqlite_ischema_names["CURVE"] = Geometry
|
||||
_sqlite_ischema_names["GEOMETRYCOLLECTION"] = Geometry
|
||||
_sqlite_ischema_names["RASTER"] = Raster
|
||||
|
||||
|
||||
class SummaryStats(CompositeType):
|
||||
"""Define the composite type returned by the function ST_SummaryStatsAgg."""
|
||||
|
||||
typemap = {
|
||||
"count": Integer,
|
||||
"sum": Float,
|
||||
"mean": Float,
|
||||
"stddev": Float,
|
||||
"min": Float,
|
||||
"max": Float,
|
||||
}
|
||||
|
||||
cache_ok = True
|
||||
""" Enable cache for this type. """
|
||||
|
||||
|
||||
__all__ = [
|
||||
"_GISType",
|
||||
"CompositeType",
|
||||
"Geography",
|
||||
"Geometry",
|
||||
"GeometryDump",
|
||||
"Raster",
|
||||
"SummaryStats",
|
||||
"dialects",
|
||||
"select_dialect",
|
||||
]
|
||||
|
||||
|
||||
def __dir__():
|
||||
return __all__
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
"""This module defines some dialect-specific functions used for Column types."""
|
||||
|
||||
from geoalchemy2.types.dialects import common # noqa
|
||||
from geoalchemy2.types.dialects import geopackage # noqa
|
||||
from geoalchemy2.types.dialects import mysql # noqa
|
||||
from geoalchemy2.types.dialects import postgresql # noqa
|
||||
from geoalchemy2.types.dialects import sqlite # noqa
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
"""This module defines functions used by several dialects."""
|
||||
|
||||
|
||||
def bind_processor_process(spatial_type, bindvalue):
|
||||
return bindvalue
|
||||
@@ -0,0 +1,3 @@
|
||||
"""This module defines specific functions for GeoPackage dialect."""
|
||||
|
||||
from geoalchemy2.types.dialects.sqlite import bind_processor_process # noqa
|
||||
@@ -0,0 +1,48 @@
|
||||
"""This module defines specific functions for MySQL dialect."""
|
||||
|
||||
from geoalchemy2.elements import WKBElement
|
||||
from geoalchemy2.elements import WKTElement
|
||||
from geoalchemy2.elements import _SpatialElement
|
||||
from geoalchemy2.exc import ArgumentError
|
||||
from geoalchemy2.shape import to_shape
|
||||
|
||||
|
||||
def bind_processor_process(spatial_type, bindvalue):
|
||||
if isinstance(bindvalue, str):
|
||||
wkt_match = WKTElement._REMOVE_SRID.match(bindvalue)
|
||||
srid = wkt_match.group(2)
|
||||
try:
|
||||
if srid is not None:
|
||||
srid = int(srid)
|
||||
except (ValueError, TypeError): # pragma: no cover
|
||||
raise ArgumentError(
|
||||
f"The SRID ({srid}) of the supplied value can not be casted to integer"
|
||||
)
|
||||
|
||||
if srid is not None and srid != spatial_type.srid:
|
||||
raise ArgumentError(
|
||||
f"The SRID ({srid}) of the supplied value is different "
|
||||
f"from the one of the column ({spatial_type.srid})"
|
||||
)
|
||||
return wkt_match.group(3)
|
||||
|
||||
if (
|
||||
isinstance(bindvalue, _SpatialElement)
|
||||
and bindvalue.srid != -1
|
||||
and bindvalue.srid != spatial_type.srid
|
||||
):
|
||||
raise ArgumentError(
|
||||
f"The SRID ({bindvalue.srid}) of the supplied value is different "
|
||||
f"from the one of the column ({spatial_type.srid})"
|
||||
)
|
||||
|
||||
if isinstance(bindvalue, WKTElement):
|
||||
bindvalue = bindvalue.as_wkt()
|
||||
if bindvalue.srid == -1:
|
||||
bindvalue.srid = spatial_type.srid
|
||||
return bindvalue
|
||||
elif isinstance(bindvalue, WKBElement):
|
||||
if "wkb" not in spatial_type.from_text.lower():
|
||||
# With MySQL we use Shapely to convert the WKBElement to an EWKT string
|
||||
return to_shape(bindvalue).wkt
|
||||
return bindvalue
|
||||
@@ -0,0 +1,29 @@
|
||||
"""This module defines specific functions for Postgresql dialect."""
|
||||
|
||||
from geoalchemy2.elements import RasterElement
|
||||
from geoalchemy2.elements import WKBElement
|
||||
from geoalchemy2.elements import WKTElement
|
||||
from geoalchemy2.shape import to_shape
|
||||
|
||||
|
||||
def bind_processor_process(spatial_type, bindvalue):
|
||||
if isinstance(bindvalue, WKTElement):
|
||||
if bindvalue.extended:
|
||||
return "%s" % (bindvalue.data)
|
||||
else:
|
||||
return "SRID=%d;%s" % (bindvalue.srid, bindvalue.data)
|
||||
elif isinstance(bindvalue, WKBElement):
|
||||
if not bindvalue.extended:
|
||||
# When the WKBElement includes a WKB value rather
|
||||
# than a EWKB value we use Shapely to convert the WKBElement to an
|
||||
# EWKT string
|
||||
shape = to_shape(bindvalue)
|
||||
return "SRID=%d;%s" % (bindvalue.srid, shape.wkt)
|
||||
else:
|
||||
# PostGIS ST_GeomFromEWKT works with EWKT strings as well
|
||||
# as EWKB hex strings
|
||||
return bindvalue.desc
|
||||
elif isinstance(bindvalue, RasterElement):
|
||||
return "%s" % (bindvalue.data)
|
||||
else:
|
||||
return bindvalue
|
||||
@@ -0,0 +1,55 @@
|
||||
"""This module defines specific functions for SQLite dialect."""
|
||||
|
||||
import re
|
||||
import warnings
|
||||
|
||||
from geoalchemy2.elements import RasterElement
|
||||
from geoalchemy2.elements import WKBElement
|
||||
from geoalchemy2.elements import WKTElement
|
||||
from geoalchemy2.shape import to_shape
|
||||
|
||||
|
||||
def format_geom_type(wkt, default_srid=None):
|
||||
"""Format the Geometry type for SQLite."""
|
||||
match = re.match(WKTElement.SPLIT_WKT_PATTERN, wkt)
|
||||
if match is None:
|
||||
warnings.warn(
|
||||
"The given WKT could not be parsed by GeoAlchemy2, this could lead to undefined "
|
||||
f"behavior with Z, M or ZM geometries or with incorrect SRID. The WKT string is: {wkt}"
|
||||
)
|
||||
return wkt
|
||||
_, srid, geom_type, coords = match.groups()
|
||||
geom_type = geom_type.replace(" ", "")
|
||||
if geom_type.endswith("ZM"):
|
||||
geom_type = geom_type[:-2]
|
||||
elif geom_type.endswith("Z"):
|
||||
geom_type = geom_type[:-1]
|
||||
if srid is None and default_srid is not None:
|
||||
srid = f"SRID={default_srid}"
|
||||
if srid is not None:
|
||||
return "%s;%s%s" % (srid, geom_type, coords)
|
||||
else:
|
||||
return "%s%s" % (geom_type, coords)
|
||||
|
||||
|
||||
def bind_processor_process(spatial_type, bindvalue):
|
||||
if isinstance(bindvalue, WKTElement):
|
||||
return format_geom_type(
|
||||
bindvalue.data,
|
||||
default_srid=bindvalue.srid if bindvalue.srid >= 0 else spatial_type.srid,
|
||||
)
|
||||
elif isinstance(bindvalue, WKBElement):
|
||||
# With SpatiaLite we use Shapely to convert the WKBElement to an EWKT string
|
||||
shape = to_shape(bindvalue)
|
||||
# shapely.wkb.loads returns geom_type with a 'Z', for example, 'LINESTRING Z'
|
||||
# which is a limitation with SpatiaLite. Hence, a temporary fix.
|
||||
res = format_geom_type(
|
||||
shape.wkt, default_srid=bindvalue.srid if bindvalue.srid >= 0 else spatial_type.srid
|
||||
)
|
||||
return res
|
||||
elif isinstance(bindvalue, RasterElement):
|
||||
return "%s" % (bindvalue.data)
|
||||
elif isinstance(bindvalue, str):
|
||||
return format_geom_type(bindvalue, default_srid=spatial_type.srid)
|
||||
else:
|
||||
return bindvalue
|
||||
17
.venv/lib/python3.12/site-packages/geoalchemy2/utils.py
Normal file
17
.venv/lib/python3.12/site-packages/geoalchemy2/utils.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""Some utils for the GeoAlchemy2 package."""
|
||||
|
||||
|
||||
def authorized_values_in_docstring(**kwargs):
|
||||
"""Decorator to replace keywords in docstrings by the actual value of a variable.
|
||||
|
||||
.. Note::
|
||||
The keyword must be enclose by <> in the docstring, like <MyKeyword>.
|
||||
"""
|
||||
|
||||
def inner(func):
|
||||
if func.__doc__ is not None:
|
||||
for k, v in kwargs.items():
|
||||
func.__doc__ = func.__doc__.replace(f"<{k}>", str(v))
|
||||
return func
|
||||
|
||||
return inner
|
||||
130
data_raw/columns.csv
Normal file
130
data_raw/columns.csv
Normal file
@@ -0,0 +1,130 @@
|
||||
0
|
||||
Project IDNumber
|
||||
Reporting Cycle Name
|
||||
Agency Name
|
||||
Program Name
|
||||
Program Description
|
||||
Sub Program Name
|
||||
Record Type
|
||||
Project Name
|
||||
Project Type
|
||||
Project Description
|
||||
Census Tract
|
||||
Address
|
||||
Lat Long
|
||||
"Senate
|
||||
District"
|
||||
"Assembly
|
||||
District"
|
||||
County
|
||||
Total Project Cost
|
||||
Total Program GGRFFunding
|
||||
Project Life Years
|
||||
Total Project GHGReductions
|
||||
Annual Project GHGReductions
|
||||
Project Count
|
||||
Fiscal Year Funding Project
|
||||
Is Benefit Disadvantaged Communities
|
||||
Disadvantaged Community Criteria
|
||||
Disadvantaged Community Need
|
||||
Disadvantaged Community Census Tracts
|
||||
Total GGRFDisadvantaged Community Funding
|
||||
Disadvantaged Community Benefits Description
|
||||
Funding Benefiting Disadvantaged Communities
|
||||
Estimated Num Vehicles In Service
|
||||
Funding Within Disadvantage Communities
|
||||
Other Project Benefits Description
|
||||
VMTReductions
|
||||
Number Of Housing Units
|
||||
Number Of Affordable Housing Units
|
||||
Estimated Number Of Trees To Be Planted
|
||||
Energy Cost Savings
|
||||
Estimated Energy Saved KWH
|
||||
Estimated Energy Saved Therms
|
||||
Estimated Water Saved Gallons
|
||||
Estimated Energy Generated KWH
|
||||
Estimated Fuel Use Reduction Gal
|
||||
Vouchers Benefiting Disadvantaged Communities
|
||||
Number Of Rebates Issued
|
||||
Rebates Within Disadvantaged Communities
|
||||
Date Operational
|
||||
Project Completion Date
|
||||
Date Imported
|
||||
Funding Recipient
|
||||
AB1550Choice
|
||||
Buffer Amount
|
||||
Buffer Count
|
||||
Is AB1550Buffer Region
|
||||
CESVersion
|
||||
CESVersion Calc
|
||||
DAC1550Amount
|
||||
DAC1550Count
|
||||
Is Benefit DAC1550Communities
|
||||
DACTable
|
||||
FG17Comm Need
|
||||
FG17Comm Need Qual
|
||||
DACCommunity Benefit Critieria Met
|
||||
Date Selected For Award
|
||||
Low Income Amount
|
||||
Low Income Count
|
||||
Low Income Housing Amount
|
||||
Low Income Housing Count
|
||||
Is Low Income Communities
|
||||
Potential Buffer Amount
|
||||
Potential Buffer Count
|
||||
Potential DAC1550Amount
|
||||
Potential DAC1550Count
|
||||
Potential Low Income Amount
|
||||
Potential Low Income Count
|
||||
Estimated Acres Preserved
|
||||
Estimated Acres Restored
|
||||
Estimated Acres Treated
|
||||
Estimated Diverted From Landfills Tons
|
||||
Renewable Fuel Generation Tons
|
||||
Wood Burning Reduction Cords
|
||||
Estimated Ridership Increases
|
||||
Black Carbon Reductions Pounds
|
||||
StateEW_DPM
|
||||
StateEW_Nox
|
||||
StateEW_PM25
|
||||
StateEW_ROG
|
||||
Diesel Pm Reductions Pounds
|
||||
NOx Reductions Pounds
|
||||
Pm25Reductions Pounds
|
||||
Rog Reductions Pounds
|
||||
Estimated Total Recycling Tons
|
||||
Estimated Waste Digested Tons
|
||||
Reclaimed Food Tons
|
||||
NUMBER OF PLANS
|
||||
POLLINATOR ACREAGE
|
||||
RESEARCH GRANT
|
||||
SCIENCE ADVANCEMENT
|
||||
SOIL BENEFIT
|
||||
TRAVEL COST SAVINGS
|
||||
EST ENERGY GEN SCF
|
||||
EST SOURCE RED TONS
|
||||
FUEL TREATMENT NUM
|
||||
EDUCATION EVENT NUM
|
||||
ENERGY AUDIT BUILDINGS
|
||||
EST DIVERT LANDFILLS TONS
|
||||
EST FUEL GEN GAL
|
||||
Voucher ID
|
||||
Voucher Name
|
||||
Voucher Description
|
||||
Direct Jobs Fte
|
||||
Indirect Jobs Fte
|
||||
Induced Jobs Fte
|
||||
Climate Adaptation
|
||||
Community Engagement
|
||||
Compost Produced Tons
|
||||
Compost Produced Tons Yr
|
||||
Net Density DUA
|
||||
Applicants Assisted
|
||||
Invasive Cover 12 Months
|
||||
Invasive Cover 36 Months
|
||||
Project Acreage
|
||||
IS IAE
|
||||
Intermediary Admin Expenses Calc
|
||||
PRIMARY_FUNDING_RECIPIENT_TYPE
|
||||
TRIBAL AFFILIATION
|
||||
PROJECT PARTNERS
|
||||
|
129
data_raw/data_types.csv
Normal file
129
data_raw/data_types.csv
Normal file
@@ -0,0 +1,129 @@
|
||||
Project IDNumber,object
|
||||
Reporting Cycle Name,object
|
||||
Agency Name,object
|
||||
Program Name,object
|
||||
Program Description,object
|
||||
Sub Program Name,object
|
||||
Record Type,object
|
||||
Project Name,object
|
||||
Project Type,object
|
||||
Project Description,object
|
||||
Census Tract,float64
|
||||
Address,object
|
||||
Lat Long,object
|
||||
"Senate
|
||||
District",object
|
||||
"Assembly
|
||||
District",object
|
||||
County,object
|
||||
Total Project Cost,int64
|
||||
Total Program GGRFFunding,int64
|
||||
Project Life Years,object
|
||||
Total Project GHGReductions,int64
|
||||
Annual Project GHGReductions,int64
|
||||
Project Count,int64
|
||||
Fiscal Year Funding Project,object
|
||||
Is Benefit Disadvantaged Communities,bool
|
||||
Disadvantaged Community Criteria,object
|
||||
Disadvantaged Community Need,object
|
||||
Disadvantaged Community Census Tracts,object
|
||||
Total GGRFDisadvantaged Community Funding,float64
|
||||
Disadvantaged Community Benefits Description,object
|
||||
Funding Benefiting Disadvantaged Communities,float64
|
||||
Estimated Num Vehicles In Service,int64
|
||||
Funding Within Disadvantage Communities,float64
|
||||
Other Project Benefits Description,object
|
||||
VMTReductions,int64
|
||||
Number Of Housing Units,int64
|
||||
Number Of Affordable Housing Units,int64
|
||||
Estimated Number Of Trees To Be Planted,int64
|
||||
Energy Cost Savings,int64
|
||||
Estimated Energy Saved KWH,int64
|
||||
Estimated Energy Saved Therms,int64
|
||||
Estimated Water Saved Gallons,int64
|
||||
Estimated Energy Generated KWH,int64
|
||||
Estimated Fuel Use Reduction Gal,int64
|
||||
Vouchers Benefiting Disadvantaged Communities,int64
|
||||
Number Of Rebates Issued,int64
|
||||
Rebates Within Disadvantaged Communities,int64
|
||||
Date Operational,object
|
||||
Project Completion Date,object
|
||||
Date Imported,object
|
||||
Funding Recipient,object
|
||||
AB1550Choice,object
|
||||
Buffer Amount,float64
|
||||
Buffer Count,float64
|
||||
Is AB1550Buffer Region,bool
|
||||
CESVersion,float64
|
||||
CESVersion Calc,int64
|
||||
DAC1550Amount,float64
|
||||
DAC1550Count,float64
|
||||
Is Benefit DAC1550Communities,bool
|
||||
DACTable,object
|
||||
FG17Comm Need,object
|
||||
FG17Comm Need Qual,object
|
||||
DACCommunity Benefit Critieria Met,object
|
||||
Date Selected For Award,object
|
||||
Low Income Amount,float64
|
||||
Low Income Count,float64
|
||||
Low Income Housing Amount,int64
|
||||
Low Income Housing Count,int64
|
||||
Is Low Income Communities,bool
|
||||
Potential Buffer Amount,int64
|
||||
Potential Buffer Count,int64
|
||||
Potential DAC1550Amount,int64
|
||||
Potential DAC1550Count,int64
|
||||
Potential Low Income Amount,int64
|
||||
Potential Low Income Count,int64
|
||||
Estimated Acres Preserved,int64
|
||||
Estimated Acres Restored,int64
|
||||
Estimated Acres Treated,float64
|
||||
Estimated Diverted From Landfills Tons,int64
|
||||
Renewable Fuel Generation Tons,int64
|
||||
Wood Burning Reduction Cords,int64
|
||||
Estimated Ridership Increases,int64
|
||||
Black Carbon Reductions Pounds,int64
|
||||
StateEW_DPM,int64
|
||||
StateEW_Nox,int64
|
||||
StateEW_PM25,int64
|
||||
StateEW_ROG,int64
|
||||
Diesel Pm Reductions Pounds,float64
|
||||
NOx Reductions Pounds,float64
|
||||
Pm25Reductions Pounds,float64
|
||||
Rog Reductions Pounds,float64
|
||||
Estimated Total Recycling Tons,int64
|
||||
Estimated Waste Digested Tons,int64
|
||||
Reclaimed Food Tons,int64
|
||||
NUMBER OF PLANS,int64
|
||||
POLLINATOR ACREAGE,int64
|
||||
RESEARCH GRANT,int64
|
||||
SCIENCE ADVANCEMENT,float64
|
||||
SOIL BENEFIT,int64
|
||||
TRAVEL COST SAVINGS,int64
|
||||
EST ENERGY GEN SCF,float64
|
||||
EST SOURCE RED TONS,float64
|
||||
FUEL TREATMENT NUM,int64
|
||||
EDUCATION EVENT NUM,int64
|
||||
ENERGY AUDIT BUILDINGS,int64
|
||||
EST DIVERT LANDFILLS TONS,int64
|
||||
EST FUEL GEN GAL,int64
|
||||
Voucher ID,object
|
||||
Voucher Name,object
|
||||
Voucher Description,object
|
||||
Direct Jobs Fte,float64
|
||||
Indirect Jobs Fte,float64
|
||||
Induced Jobs Fte,float64
|
||||
Climate Adaptation,object
|
||||
Community Engagement,object
|
||||
Compost Produced Tons,int64
|
||||
Compost Produced Tons Yr,int64
|
||||
Net Density DUA,float64
|
||||
Applicants Assisted,int64
|
||||
Invasive Cover 12 Months,int64
|
||||
Invasive Cover 36 Months,int64
|
||||
Project Acreage,int64
|
||||
IS IAE,bool
|
||||
Intermediary Admin Expenses Calc,int64
|
||||
PRIMARY_FUNDING_RECIPIENT_TYPE,object
|
||||
TRIBAL AFFILIATION,object
|
||||
PROJECT PARTNERS,object
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user