GeoAlchemy2 0.15.2__py3-none-any.whl → 0.17.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {GeoAlchemy2-0.15.2.dist-info → GeoAlchemy2-0.17.0.dist-info}/METADATA +16 -4
- GeoAlchemy2-0.17.0.dist-info/RECORD +35 -0
- {GeoAlchemy2-0.15.2.dist-info → GeoAlchemy2-0.17.0.dist-info}/WHEEL +1 -1
- GeoAlchemy2-0.17.0.dist-info/entry_points.txt +2 -0
- geoalchemy2/__init__.py +2 -0
- geoalchemy2/admin/__init__.py +4 -4
- geoalchemy2/admin/dialects/__init__.py +1 -0
- geoalchemy2/admin/dialects/common.py +5 -5
- geoalchemy2/admin/dialects/geopackage.py +7 -3
- geoalchemy2/admin/dialects/mariadb.py +134 -0
- geoalchemy2/admin/dialects/mysql.py +27 -11
- geoalchemy2/admin/dialects/sqlite.py +10 -5
- geoalchemy2/admin/plugin.py +98 -0
- geoalchemy2/shape.py +0 -1
- geoalchemy2/types/__init__.py +7 -7
- geoalchemy2/types/dialects/__init__.py +1 -0
- geoalchemy2/types/dialects/common.py +1 -1
- geoalchemy2/types/dialects/mariadb.py +47 -0
- GeoAlchemy2-0.15.2.dist-info/RECORD +0 -31
- {GeoAlchemy2-0.15.2.dist-info → GeoAlchemy2-0.17.0.dist-info}/COPYING.rst +0 -0
- {GeoAlchemy2-0.15.2.dist-info → GeoAlchemy2-0.17.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: GeoAlchemy2
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.17.0
|
4
4
|
Summary: Using SQLAlchemy with Spatial Databases
|
5
5
|
Home-page: https://geoalchemy-2.readthedocs.io/en/stable/
|
6
6
|
Author: Eric Lemoine
|
@@ -24,10 +24,22 @@ Classifier: License :: OSI Approved :: MIT License
|
|
24
24
|
Classifier: Topic :: Scientific/Engineering :: GIS
|
25
25
|
Requires-Python: >=3.7
|
26
26
|
License-File: COPYING.rst
|
27
|
-
Requires-Dist: SQLAlchemy
|
27
|
+
Requires-Dist: SQLAlchemy>=1.4
|
28
28
|
Requires-Dist: packaging
|
29
29
|
Provides-Extra: shapely
|
30
|
-
Requires-Dist: Shapely
|
30
|
+
Requires-Dist: Shapely>=1.7; extra == "shapely"
|
31
|
+
Dynamic: author
|
32
|
+
Dynamic: author-email
|
33
|
+
Dynamic: classifier
|
34
|
+
Dynamic: description
|
35
|
+
Dynamic: home-page
|
36
|
+
Dynamic: keywords
|
37
|
+
Dynamic: license
|
38
|
+
Dynamic: project-url
|
39
|
+
Dynamic: provides-extra
|
40
|
+
Dynamic: requires-dist
|
41
|
+
Dynamic: requires-python
|
42
|
+
Dynamic: summary
|
31
43
|
|
32
44
|
============
|
33
45
|
GeoAlchemy 2
|
@@ -0,0 +1,35 @@
|
|
1
|
+
geoalchemy2/__init__.py,sha256=aGo3WcjOBcqrvqYXBuzCff1d_WiFUFObgtcyR11gg6M,2043
|
2
|
+
geoalchemy2/_functions.py,sha256=e8v584Fx_fmh8XnJvkuYEXrcyPDiWV95Mu2yw6L-LHY,62863
|
3
|
+
geoalchemy2/_functions_helpers.py,sha256=hVM9DDTgf-oM27EZu6ycW1y2ENnSxUJixdINetWsu0E,2651
|
4
|
+
geoalchemy2/alembic_helpers.py,sha256=WYjKiVVyHtlB4DI22z-G0-x8NnG8othAINUwmxBCMcs,27885
|
5
|
+
geoalchemy2/comparator.py,sha256=WUDXn10doDlJVnYiWbVIAhhBu7N5AO9BI8sNf2mhpA4,8100
|
6
|
+
geoalchemy2/elements.py,sha256=5yd_7dUQGbrLvgYvo6A2YYAFwTvP4_BskIdBCB0DrlM,13002
|
7
|
+
geoalchemy2/exc.py,sha256=Nn9bRKB_35skWDMkEf4_Y2GL6gvguPVycPZcbOfa69g,226
|
8
|
+
geoalchemy2/functions.py,sha256=0he8hy_SAWpXAFPdfg32NMW5G0FaZP7yVl0jn0MRLBQ,10320
|
9
|
+
geoalchemy2/functions.pyi,sha256=rKCKdDSVTgeUQHItkOKqJSEuyKmVJ30bpjLpCPwMI5g,108636
|
10
|
+
geoalchemy2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
+
geoalchemy2/shape.py,sha256=TLeWa6NwXZqBxh3Zj-l96wBB5kPviEhnZ44kyr8v678,2735
|
12
|
+
geoalchemy2/utils.py,sha256=OYWYnT64tjp4DWhPdq3KIxHbVti932xPMtGGxajtu-I,488
|
13
|
+
geoalchemy2/admin/__init__.py,sha256=O5kr1ETm3lTpEELm16Oxq6344KztOZmUaapf0ZIP6b8,4016
|
14
|
+
geoalchemy2/admin/plugin.py,sha256=2U5p4_JdZM7b2MOXXBfP8KhtXlLHu-ASjxTq4mF-thg,4332
|
15
|
+
geoalchemy2/admin/dialects/__init__.py,sha256=z4gBrdglClnmp9VdDW0WwEAGNoemMUSvMCeFFvuQcgs,422
|
16
|
+
geoalchemy2/admin/dialects/common.py,sha256=p-CGtcooIESy-BiwZ8bmmdAORjuJtTK-sq_X0jnjHB0,3036
|
17
|
+
geoalchemy2/admin/dialects/geopackage.py,sha256=PgOhRftDN4I_OIqqOfop7jgmospReCq6qJi6BGW-Gts,13596
|
18
|
+
geoalchemy2/admin/dialects/mariadb.py,sha256=4PtkC1Gv4jcAl_grO5lJwxPsIChi4_8g36soZXnMM3w,4267
|
19
|
+
geoalchemy2/admin/dialects/mysql.py,sha256=NP0qXGsvssHZKegP8m8-4Vqbx8CosNHTsq6qfSD2MHs,7181
|
20
|
+
geoalchemy2/admin/dialects/postgresql.py,sha256=VwB_h3TC8M5aQ6aKE3UYgxHfbEKav3eIHJeLx534Zzg,6191
|
21
|
+
geoalchemy2/admin/dialects/sqlite.py,sha256=iW5MBhbxVKGMVghN5Do0iF-_9Pbtrv65NTXmW9mOyZA,13802
|
22
|
+
geoalchemy2/types/__init__.py,sha256=Ud1_6gukAs3gSzN3BThZnC-p0naHnTcNEjlyD5Ahm-M,14240
|
23
|
+
geoalchemy2/types/dialects/__init__.py,sha256=GYqO6nDtzElvsV22VN1SSUet2elaZG4ZuRrM-asbstM,414
|
24
|
+
geoalchemy2/types/dialects/common.py,sha256=eiIKe-sFAdzgvQ7YT0cV29tYAPrs55GXKJi3p1UvjPQ,158
|
25
|
+
geoalchemy2/types/dialects/geopackage.py,sha256=nRmN_PnF-CWMEHkWhKVdsybnw3SvXZIBXAHIHXJLTqw,147
|
26
|
+
geoalchemy2/types/dialects/mariadb.py,sha256=tPZpfj95XlVSOm8zC52iFbqVrIOdj-EDH5KVJP1Gd9c,1723
|
27
|
+
geoalchemy2/types/dialects/mysql.py,sha256=zMNi1920v0XlVaCJWSwtq0b0P5YQRn8y3X_rBxC5KEw,1790
|
28
|
+
geoalchemy2/types/dialects/postgresql.py,sha256=7NBKEbDJXMwX8Sgs6o_N2bAUHgjUcjbrdmYOA7sDiRw,1117
|
29
|
+
geoalchemy2/types/dialects/sqlite.py,sha256=B-yLzaQcqL_4dXoOPX9D9IXw2ZQlq-Tibv_kqUIBeb4,2104
|
30
|
+
GeoAlchemy2-0.17.0.dist-info/COPYING.rst,sha256=-bQKftq9uMOROzF7oN65kYBBIJKxTmBuDoftp27IC3I,1056
|
31
|
+
GeoAlchemy2-0.17.0.dist-info/METADATA,sha256=mc9wg4zIYUEuRbFYELFA4LygDIo6hKXdM9CDR-KyHQo,2327
|
32
|
+
GeoAlchemy2-0.17.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
33
|
+
GeoAlchemy2-0.17.0.dist-info/entry_points.txt,sha256=izFHecGE8cNV6IjoLkc0uEmKH13rYbbvozc4fEwRl6Y,70
|
34
|
+
GeoAlchemy2-0.17.0.dist-info/top_level.txt,sha256=3kGUTcfBeXd61zFpof6-qiuw1peNF_HuZabgHQgrdis,12
|
35
|
+
GeoAlchemy2-0.17.0.dist-info/RECORD,,
|
geoalchemy2/__init__.py
CHANGED
@@ -8,6 +8,7 @@ from geoalchemy2 import shape # noqa
|
|
8
8
|
from geoalchemy2 import types # noqa
|
9
9
|
from geoalchemy2.admin.dialects.geopackage import load_spatialite_gpkg # noqa
|
10
10
|
from geoalchemy2.admin.dialects.sqlite import load_spatialite # noqa
|
11
|
+
from geoalchemy2.admin.plugin import GeoEngine # noqa
|
11
12
|
from geoalchemy2.elements import CompositeElement # noqa
|
12
13
|
from geoalchemy2.elements import RasterElement # noqa
|
13
14
|
from geoalchemy2.elements import WKBElement # noqa
|
@@ -50,6 +51,7 @@ __all__ = [
|
|
50
51
|
"__version__",
|
51
52
|
"ArgumentError",
|
52
53
|
"CompositeElement",
|
54
|
+
"GeoEngine",
|
53
55
|
"Geography",
|
54
56
|
"Geometry",
|
55
57
|
"Raster",
|
geoalchemy2/admin/__init__.py
CHANGED
@@ -20,7 +20,7 @@ def select_dialect(dialect_name):
|
|
20
20
|
known_dialects = {
|
21
21
|
"geopackage": dialects.geopackage,
|
22
22
|
"mysql": dialects.mysql,
|
23
|
-
"mariadb": dialects.
|
23
|
+
"mariadb": dialects.mariadb,
|
24
24
|
"postgresql": dialects.postgresql,
|
25
25
|
"sqlite": dialects.sqlite,
|
26
26
|
}
|
@@ -53,7 +53,7 @@ def setup_ddl_event_listeners():
|
|
53
53
|
@event.listens_for(Column, "after_parent_attach")
|
54
54
|
def after_parent_attach(column, table):
|
55
55
|
"""Automatically add spatial indexes."""
|
56
|
-
if not isinstance(table, Table):
|
56
|
+
if not isinstance(table, Table): # pragma: no cover
|
57
57
|
# For old versions of SQLAlchemy, subqueries might trigger the after_parent_attach event
|
58
58
|
# with a selectable as table, so we want to skip this case.
|
59
59
|
return
|
@@ -75,7 +75,7 @@ def setup_ddl_event_listeners():
|
|
75
75
|
try:
|
76
76
|
if column.type._spatial_index_reflected:
|
77
77
|
return
|
78
|
-
except AttributeError:
|
78
|
+
except AttributeError: # pragma: no cover
|
79
79
|
pass
|
80
80
|
|
81
81
|
kwargs = {
|
@@ -98,7 +98,7 @@ def setup_ddl_event_listeners():
|
|
98
98
|
)
|
99
99
|
|
100
100
|
@event.listens_for(Table, "column_reflect")
|
101
|
-
def
|
101
|
+
def column_reflect(inspector, table, column_info):
|
102
102
|
select_dialect(inspector.bind.dialect.name).reflect_geometry_column(
|
103
103
|
inspector, table, column_info
|
104
104
|
)
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
from geoalchemy2.admin.dialects import common # noqa
|
4
4
|
from geoalchemy2.admin.dialects import geopackage # noqa
|
5
|
+
from geoalchemy2.admin.dialects import mariadb # noqa
|
5
6
|
from geoalchemy2.admin.dialects import mysql # noqa
|
6
7
|
from geoalchemy2.admin.dialects import postgresql # noqa
|
7
8
|
from geoalchemy2.admin.dialects import sqlite # noqa
|
@@ -85,20 +85,20 @@ def setup_create_drop(table, bind, check_col_management=None):
|
|
85
85
|
|
86
86
|
|
87
87
|
def reflect_geometry_column(inspector, table, column_info):
|
88
|
-
return
|
88
|
+
return # pragma: no cover
|
89
89
|
|
90
90
|
|
91
91
|
def before_create(table, bind, **kw):
|
92
|
-
return
|
92
|
+
return # pragma: no cover
|
93
93
|
|
94
94
|
|
95
95
|
def after_create(table, bind, **kw):
|
96
|
-
return
|
96
|
+
return # pragma: no cover
|
97
97
|
|
98
98
|
|
99
99
|
def before_drop(table, bind, **kw):
|
100
|
-
return
|
100
|
+
return # pragma: no cover
|
101
101
|
|
102
102
|
|
103
103
|
def after_drop(table, bind, **kw):
|
104
|
-
return
|
104
|
+
return # pragma: no cover
|
@@ -71,15 +71,15 @@ def init_geopackage(dbapi_conn, *args):
|
|
71
71
|
dbapi_conn.execute("SELECT gpkgCreateBaseTables();")
|
72
72
|
|
73
73
|
|
74
|
-
def load_spatialite_gpkg(*args, **kwargs):
|
74
|
+
def load_spatialite_gpkg(dbapi_conn, *args, **kwargs):
|
75
75
|
"""Load SpatiaLite extension in GeoPackage and initialize internal tables.
|
76
76
|
|
77
77
|
See :func:`geoalchemy2.admin.dialects.geopackage.load_geopackage_driver` and
|
78
78
|
:func:`geoalchemy2.admin.dialects.geopackage.init_geopackage` functions for details about
|
79
79
|
arguments.
|
80
80
|
"""
|
81
|
-
load_geopackage_driver(
|
82
|
-
init_geopackage(
|
81
|
+
load_geopackage_driver(dbapi_conn)
|
82
|
+
init_geopackage(dbapi_conn, **kwargs)
|
83
83
|
|
84
84
|
|
85
85
|
def _get_spatialite_attrs(bind, table_name, col_name):
|
@@ -188,6 +188,10 @@ def reflect_geometry_column(inspector, table, column_info):
|
|
188
188
|
column_info["type"]._spatial_index_reflected = False
|
189
189
|
|
190
190
|
|
191
|
+
def connect(dbapi_conn, *args, **kwargs):
|
192
|
+
return load_spatialite_gpkg(dbapi_conn, *args, **kwargs)
|
193
|
+
|
194
|
+
|
191
195
|
def before_create(table, bind, **kw):
|
192
196
|
"""Handle spatial indexes during the before_create event."""
|
193
197
|
dialect, gis_cols, regular_cols = setup_create_drop(table, bind)
|
@@ -0,0 +1,134 @@
|
|
1
|
+
"""This module defines specific functions for MariaDB dialect."""
|
2
|
+
|
3
|
+
from sqlalchemy.ext.compiler import compiles
|
4
|
+
|
5
|
+
from geoalchemy2 import functions
|
6
|
+
from geoalchemy2.admin.dialects.mysql import after_create # noqa
|
7
|
+
from geoalchemy2.admin.dialects.mysql import after_drop # noqa
|
8
|
+
from geoalchemy2.admin.dialects.mysql import before_create # noqa
|
9
|
+
from geoalchemy2.admin.dialects.mysql import before_drop # noqa
|
10
|
+
from geoalchemy2.admin.dialects.mysql import reflect_geometry_column # noqa
|
11
|
+
from geoalchemy2.elements import WKBElement
|
12
|
+
from geoalchemy2.elements import WKTElement
|
13
|
+
from geoalchemy2.shape import to_shape
|
14
|
+
|
15
|
+
|
16
|
+
def _cast(param):
|
17
|
+
if isinstance(param, memoryview):
|
18
|
+
param = param.tobytes()
|
19
|
+
if isinstance(param, bytes):
|
20
|
+
data_element = WKBElement(param)
|
21
|
+
param = to_shape(data_element).wkt.encode("utf-8")
|
22
|
+
return param
|
23
|
+
|
24
|
+
|
25
|
+
def before_cursor_execute(
|
26
|
+
conn, cursor, statement, parameters, context, executemany, convert=True
|
27
|
+
): # noqa: D417
|
28
|
+
"""Event handler to cast the parameters properly.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
convert (bool): Trigger the conversion.
|
32
|
+
"""
|
33
|
+
if convert:
|
34
|
+
if isinstance(parameters, (tuple, list)):
|
35
|
+
parameters = tuple(_cast(x) for x in parameters)
|
36
|
+
elif isinstance(parameters, dict):
|
37
|
+
for k in parameters:
|
38
|
+
parameters[k] = _cast(parameters[k])
|
39
|
+
|
40
|
+
return statement, parameters
|
41
|
+
|
42
|
+
|
43
|
+
_MARIADB_FUNCTIONS = {
|
44
|
+
"ST_AsEWKB": "ST_AsBinary",
|
45
|
+
}
|
46
|
+
|
47
|
+
|
48
|
+
def _compiles_mariadb(cls, fn):
|
49
|
+
def _compile_mariadb(element, compiler, **kw):
|
50
|
+
return "{}({})".format(fn, compiler.process(element.clauses, **kw))
|
51
|
+
|
52
|
+
compiles(getattr(functions, cls), "mariadb")(_compile_mariadb)
|
53
|
+
|
54
|
+
|
55
|
+
def register_mariadb_mapping(mapping):
|
56
|
+
"""Register compilation mappings for the given functions.
|
57
|
+
|
58
|
+
Args:
|
59
|
+
mapping: Should have the following form::
|
60
|
+
|
61
|
+
{
|
62
|
+
"function_name_1": "mariadb_function_name_1",
|
63
|
+
"function_name_2": "mariadb_function_name_2",
|
64
|
+
...
|
65
|
+
}
|
66
|
+
"""
|
67
|
+
for cls, fn in mapping.items():
|
68
|
+
_compiles_mariadb(cls, fn)
|
69
|
+
|
70
|
+
|
71
|
+
register_mariadb_mapping(_MARIADB_FUNCTIONS)
|
72
|
+
|
73
|
+
|
74
|
+
def _compile_GeomFromText_MariaDB(element, compiler, **kw):
|
75
|
+
element.identifier = "ST_GeomFromText"
|
76
|
+
compiled = compiler.process(element.clauses, **kw)
|
77
|
+
try:
|
78
|
+
clauses = list(element.clauses)
|
79
|
+
data_element = WKTElement(clauses[0].value)
|
80
|
+
srid = max(0, data_element.srid)
|
81
|
+
if srid <= 0:
|
82
|
+
srid = max(0, element.type.srid)
|
83
|
+
if len(clauses) > 1 and srid > 0:
|
84
|
+
clauses[1].value = srid
|
85
|
+
except Exception:
|
86
|
+
srid = max(0, element.type.srid)
|
87
|
+
|
88
|
+
if srid > 0:
|
89
|
+
res = "{}({}, {})".format(element.identifier, compiled, srid)
|
90
|
+
else:
|
91
|
+
res = "{}({})".format(element.identifier, compiled)
|
92
|
+
return res
|
93
|
+
|
94
|
+
|
95
|
+
def _compile_GeomFromWKB_MariaDB(element, compiler, **kw):
|
96
|
+
element.identifier = "ST_GeomFromText"
|
97
|
+
|
98
|
+
try:
|
99
|
+
clauses = list(element.clauses)
|
100
|
+
data_element = WKBElement(clauses[0].value)
|
101
|
+
srid = max(0, data_element.srid)
|
102
|
+
if srid <= 0:
|
103
|
+
srid = max(0, element.type.srid)
|
104
|
+
if len(clauses) > 1 and srid > 0:
|
105
|
+
clauses[1].value = srid
|
106
|
+
except Exception:
|
107
|
+
srid = max(0, element.type.srid)
|
108
|
+
compiled = compiler.process(element.clauses, **kw)
|
109
|
+
|
110
|
+
if srid > 0:
|
111
|
+
res = "{}({}, {})".format(element.identifier, compiled, srid)
|
112
|
+
else:
|
113
|
+
res = "{}({})".format(element.identifier, compiled)
|
114
|
+
return res
|
115
|
+
|
116
|
+
|
117
|
+
@compiles(functions.ST_GeomFromText, "mariadb") # type: ignore
|
118
|
+
def _MariaDB_ST_GeomFromText(element, compiler, **kw):
|
119
|
+
return _compile_GeomFromText_MariaDB(element, compiler, **kw)
|
120
|
+
|
121
|
+
|
122
|
+
@compiles(functions.ST_GeomFromEWKT, "mariadb") # type: ignore
|
123
|
+
def _MariaDB_ST_GeomFromEWKT(element, compiler, **kw):
|
124
|
+
return _compile_GeomFromText_MariaDB(element, compiler, **kw)
|
125
|
+
|
126
|
+
|
127
|
+
@compiles(functions.ST_GeomFromWKB, "mariadb") # type: ignore
|
128
|
+
def _MariaDB_ST_GeomFromWKB(element, compiler, **kw):
|
129
|
+
return _compile_GeomFromWKB_MariaDB(element, compiler, **kw)
|
130
|
+
|
131
|
+
|
132
|
+
@compiles(functions.ST_GeomFromEWKB, "mariadb") # type: ignore
|
133
|
+
def _MariaDB_ST_GeomFromEWKB(element, compiler, **kw):
|
134
|
+
return _compile_GeomFromWKB_MariaDB(element, compiler, **kw)
|
@@ -31,11 +31,16 @@ def reflect_geometry_column(inspector, table, column_info):
|
|
31
31
|
column_name = column_info.get("name")
|
32
32
|
schema = table.schema or inspector.default_schema_name
|
33
33
|
|
34
|
+
if inspector.dialect.name == "mariadb":
|
35
|
+
select_srid = "-1, "
|
36
|
+
else:
|
37
|
+
select_srid = "SRS_ID, "
|
38
|
+
|
34
39
|
# Check geometry type, SRID and if the column is nullable
|
35
|
-
geometry_type_query = """SELECT DATA_TYPE,
|
40
|
+
geometry_type_query = """SELECT DATA_TYPE, {}IS_NULLABLE
|
36
41
|
FROM INFORMATION_SCHEMA.COLUMNS
|
37
42
|
WHERE TABLE_NAME = '{}' and COLUMN_NAME = '{}'""".format(
|
38
|
-
table.name, column_name
|
43
|
+
select_srid, table.name, column_name
|
39
44
|
)
|
40
45
|
if schema is not None:
|
41
46
|
geometry_type_query += """ and table_schema = '{}'""".format(schema)
|
@@ -43,7 +48,7 @@ def reflect_geometry_column(inspector, table, column_info):
|
|
43
48
|
is_nullable = str(nullable_str).lower() == "yes"
|
44
49
|
|
45
50
|
if geometry_type not in _POSSIBLE_TYPES:
|
46
|
-
return
|
51
|
+
return # pragma: no cover
|
47
52
|
|
48
53
|
# Check if the column has spatial index
|
49
54
|
has_index_query = """SELECT DISTINCT
|
@@ -67,6 +72,25 @@ def reflect_geometry_column(inspector, table, column_info):
|
|
67
72
|
)
|
68
73
|
|
69
74
|
|
75
|
+
def before_cursor_execute(
|
76
|
+
conn, cursor, statement, parameters, context, executemany, convert=True
|
77
|
+
): # noqa: D417
|
78
|
+
"""Event handler to cast the parameters properly.
|
79
|
+
|
80
|
+
Args:
|
81
|
+
convert (bool): Trigger the conversion.
|
82
|
+
"""
|
83
|
+
if convert:
|
84
|
+
if isinstance(parameters, (tuple, list)):
|
85
|
+
parameters = tuple(x.tobytes() if isinstance(x, memoryview) else x for x in parameters)
|
86
|
+
elif isinstance(parameters, dict):
|
87
|
+
for k in parameters:
|
88
|
+
if isinstance(parameters[k], memoryview):
|
89
|
+
parameters[k] = parameters[k].tobytes()
|
90
|
+
|
91
|
+
return statement, parameters
|
92
|
+
|
93
|
+
|
70
94
|
def before_create(table, bind, **kw):
|
71
95
|
"""Handle spatial indexes during the before_create event."""
|
72
96
|
dialect, gis_cols, regular_cols = setup_create_drop(table, bind)
|
@@ -129,7 +153,6 @@ def _compiles_mysql(cls, fn):
|
|
129
153
|
return "{}({})".format(fn, compiler.process(element.clauses, **kw))
|
130
154
|
|
131
155
|
compiles(getattr(functions, cls), "mysql")(_compile_mysql)
|
132
|
-
compiles(getattr(functions, cls), "mariadb")(_compile_mysql)
|
133
156
|
|
134
157
|
|
135
158
|
def register_mysql_mapping(mapping):
|
@@ -164,9 +187,6 @@ def _compile_GeomFromText_MySql(element, compiler, **kw):
|
|
164
187
|
|
165
188
|
def _compile_GeomFromWKB_MySql(element, compiler, **kw):
|
166
189
|
element.identifier = "ST_GeomFromWKB"
|
167
|
-
wkb_data = list(element.clauses)[0].value
|
168
|
-
if isinstance(wkb_data, memoryview):
|
169
|
-
list(element.clauses)[0].value = wkb_data.tobytes()
|
170
190
|
compiled = compiler.process(element.clauses, **kw)
|
171
191
|
srid = element.type.srid
|
172
192
|
|
@@ -177,24 +197,20 @@ def _compile_GeomFromWKB_MySql(element, compiler, **kw):
|
|
177
197
|
|
178
198
|
|
179
199
|
@compiles(functions.ST_GeomFromText, "mysql") # type: ignore
|
180
|
-
@compiles(functions.ST_GeomFromText, "mariadb") # type: ignore
|
181
200
|
def _MySQL_ST_GeomFromText(element, compiler, **kw):
|
182
201
|
return _compile_GeomFromText_MySql(element, compiler, **kw)
|
183
202
|
|
184
203
|
|
185
204
|
@compiles(functions.ST_GeomFromEWKT, "mysql") # type: ignore
|
186
|
-
@compiles(functions.ST_GeomFromEWKT, "mariadb") # type: ignore
|
187
205
|
def _MySQL_ST_GeomFromEWKT(element, compiler, **kw):
|
188
206
|
return _compile_GeomFromText_MySql(element, compiler, **kw)
|
189
207
|
|
190
208
|
|
191
209
|
@compiles(functions.ST_GeomFromWKB, "mysql") # type: ignore
|
192
|
-
@compiles(functions.ST_GeomFromWKB, "mariadb") # type: ignore
|
193
210
|
def _MySQL_ST_GeomFromWKB(element, compiler, **kw):
|
194
211
|
return _compile_GeomFromWKB_MySql(element, compiler, **kw)
|
195
212
|
|
196
213
|
|
197
214
|
@compiles(functions.ST_GeomFromEWKB, "mysql") # type: ignore
|
198
|
-
@compiles(functions.ST_GeomFromEWKB, "mariadb") # type: ignore
|
199
215
|
def _MySQL_ST_GeomFromEWKB(element, compiler, **kw):
|
200
216
|
return _compile_GeomFromWKB_MySql(element, compiler, **kw)
|
@@ -51,6 +51,8 @@ def init_spatialite(
|
|
51
51
|
|
52
52
|
Args:
|
53
53
|
dbapi_conn: The DBAPI connection.
|
54
|
+
transaction: If set to `True` the whole operation will be handled as a single Transaction
|
55
|
+
(faster). The default value is `False` (slower, but safer).
|
54
56
|
init_mode: Can be `None` to load all EPSG SRIDs, `'WGS84'` to load only the ones related
|
55
57
|
to WGS84 or `'EMPTY'` to not load any EPSG SRID.
|
56
58
|
|
@@ -58,8 +60,6 @@ def init_spatialite(
|
|
58
60
|
|
59
61
|
It is possible to load other EPSG SRIDs afterwards using `InsertEpsgSrid(srid)`.
|
60
62
|
|
61
|
-
transaction: If set to `True` the whole operation will be handled as a single Transaction
|
62
|
-
(faster). The default value is `False` (slower, but safer).
|
63
63
|
journal_mode: Change the journal mode to the given value. This can make the table creation
|
64
64
|
much faster. The possible values are the following: <JOURNAL_MODE_VALUES>. See
|
65
65
|
https://www.sqlite.org/pragma.html#pragma_journal_mode for more details.
|
@@ -127,15 +127,15 @@ def init_spatialite(
|
|
127
127
|
dbapi_conn.execute("PRAGMA journal_mode = {}".format(current_journal_mode))
|
128
128
|
|
129
129
|
|
130
|
-
def load_spatialite(*args, **kwargs):
|
130
|
+
def load_spatialite(dbapi_conn, *args, **kwargs):
|
131
131
|
"""Load SpatiaLite extension in SQLite DB and initialize internal tables.
|
132
132
|
|
133
133
|
See :func:`geoalchemy2.admin.dialects.sqlite.load_spatialite_driver` and
|
134
134
|
:func:`geoalchemy2.admin.dialects.sqlite.init_spatialite` functions for details about
|
135
135
|
arguments.
|
136
136
|
"""
|
137
|
-
load_spatialite_driver(
|
138
|
-
init_spatialite(
|
137
|
+
load_spatialite_driver(dbapi_conn)
|
138
|
+
init_spatialite(dbapi_conn, **kwargs)
|
139
139
|
|
140
140
|
|
141
141
|
def _get_spatialite_attrs(bind, table_name, col_name):
|
@@ -260,6 +260,11 @@ def reflect_geometry_column(inspector, table, column_info):
|
|
260
260
|
column_info["type"]._spatial_index_reflected = False
|
261
261
|
|
262
262
|
|
263
|
+
def connect(dbapi_conn, *args, **kwargs):
|
264
|
+
"""Even handler to load spatial extension when a new connection is created."""
|
265
|
+
return load_spatialite(dbapi_conn, *args, **kwargs)
|
266
|
+
|
267
|
+
|
263
268
|
def before_create(table, bind, **kw):
|
264
269
|
"""Handle spatial indexes during the before_create event."""
|
265
270
|
dialect, gis_cols, regular_cols = setup_create_drop(table, bind)
|
@@ -0,0 +1,98 @@
|
|
1
|
+
from collections import defaultdict
|
2
|
+
from functools import partial
|
3
|
+
|
4
|
+
from sqlalchemy import event
|
5
|
+
from sqlalchemy.engine import CreateEnginePlugin
|
6
|
+
|
7
|
+
from geoalchemy2.admin import select_dialect
|
8
|
+
|
9
|
+
|
10
|
+
class GeoEngine(CreateEnginePlugin):
|
11
|
+
"""A plugin to create `sqlalchemy.Engine` objects with specific events to handle spatial data.
|
12
|
+
|
13
|
+
This plugin should only be used with the :func:`sqlalchemy.create_engine` function. This plugin
|
14
|
+
will automatically attach the relevant event listeners to the new engine depending on its
|
15
|
+
dialect. For some specific dialects it's even possible to pass arguments to customize the
|
16
|
+
functions called by the listeners. These arguments have the following form:
|
17
|
+
`geoalchemy2_<event>_<dialect>_<argument_name>`.
|
18
|
+
For example, creating a new SQLite DB and initialize the SpatiaLite extension with only the
|
19
|
+
WGS84 SRID can be performed this way:
|
20
|
+
|
21
|
+
.. code-block:: python
|
22
|
+
|
23
|
+
db_url = "sqlite:////tmp/test_db.sqlite?geoalchemy2_connect_sqlite_init_mode=WGS84"
|
24
|
+
engine = sqlalchemy.create_engine(db_url, plugins=["geoalchemy2"])
|
25
|
+
|
26
|
+
The names of the parameters can be found in the event listener of each dialect. Note that all
|
27
|
+
dialects don't have listeners for all events.
|
28
|
+
"""
|
29
|
+
|
30
|
+
def __init__(self, url, kwargs):
|
31
|
+
super().__init__(url, kwargs)
|
32
|
+
|
33
|
+
# Consume the parameters from the URL query
|
34
|
+
self.params = defaultdict(lambda: defaultdict(dict))
|
35
|
+
|
36
|
+
transaction = url.query.get("geoalchemy2_connect_sqlite_transaction", None)
|
37
|
+
if transaction is not None:
|
38
|
+
self.params["connect"]["sqlite"]["transaction"] = self.str_to_bool(transaction)
|
39
|
+
|
40
|
+
init_mode = url.query.get("geoalchemy2_connect_sqlite_init_mode", None)
|
41
|
+
if init_mode is not None:
|
42
|
+
self.params["connect"]["sqlite"]["init_mode"] = init_mode
|
43
|
+
|
44
|
+
journal_mode = url.query.get("geoalchemy2_connect_sqlite_journal_mode", None)
|
45
|
+
if journal_mode is not None:
|
46
|
+
self.params["connect"]["sqlite"]["journal_mode"] = journal_mode
|
47
|
+
|
48
|
+
before_cursor_execute_convert_mysql = url.query.get(
|
49
|
+
"geoalchemy2_before_cursor_execute_mysql_convert", None
|
50
|
+
)
|
51
|
+
if before_cursor_execute_convert_mysql is not None:
|
52
|
+
self.params["before_cursor_execute"]["mysql"]["convert"] = self.str_to_bool(
|
53
|
+
before_cursor_execute_convert_mysql
|
54
|
+
)
|
55
|
+
|
56
|
+
before_cursor_execute_convert_mariadb = url.query.get(
|
57
|
+
"geoalchemy2_before_cursor_execute_mariadb_convert", None
|
58
|
+
)
|
59
|
+
if before_cursor_execute_convert_mariadb is not None:
|
60
|
+
self.params["before_cursor_execute"]["mariadb"]["convert"] = self.str_to_bool(
|
61
|
+
before_cursor_execute_convert_mariadb
|
62
|
+
)
|
63
|
+
|
64
|
+
@staticmethod
|
65
|
+
def str_to_bool(argument):
|
66
|
+
"""Cast argument to bool."""
|
67
|
+
lowered = str(argument).lower()
|
68
|
+
if lowered in ("yes", "y", "true", "t", "1", "enable", "on"):
|
69
|
+
return True
|
70
|
+
elif lowered in ("no", "n", "false", "f", "0", "disable", "off"):
|
71
|
+
return False
|
72
|
+
raise ValueError(argument)
|
73
|
+
|
74
|
+
def update_url(self, url):
|
75
|
+
"""Update the URL to one that no longer includes specific parameters."""
|
76
|
+
return url.difference_update_query(
|
77
|
+
[
|
78
|
+
"geoalchemy2_connect_sqlite_transaction",
|
79
|
+
"geoalchemy2_connect_sqlite_init_mode",
|
80
|
+
"geoalchemy2_connect_sqlite_journal_mode",
|
81
|
+
"geoalchemy2_before_cursor_execute_mysql_convert",
|
82
|
+
"geoalchemy2_before_cursor_execute_mariadb_convert",
|
83
|
+
],
|
84
|
+
)
|
85
|
+
|
86
|
+
def engine_created(self, engine):
|
87
|
+
"""Attach event listeners after the new Engine object is created."""
|
88
|
+
dialect_module = select_dialect(engine.dialect.name)
|
89
|
+
|
90
|
+
if hasattr(dialect_module, "connect"):
|
91
|
+
params = dict(self.params["connect"].get(engine.dialect.name, {}))
|
92
|
+
func = partial(dialect_module.connect, **params)
|
93
|
+
event.listen(engine, "connect", func)
|
94
|
+
|
95
|
+
if hasattr(dialect_module, "before_cursor_execute"):
|
96
|
+
params = dict(self.params["before_cursor_execute"].get(engine.dialect.name, {}))
|
97
|
+
func = partial(dialect_module.before_cursor_execute, **params)
|
98
|
+
event.listen(engine, "before_cursor_execute", func, retval=True)
|
geoalchemy2/shape.py
CHANGED
@@ -48,7 +48,6 @@ def to_shape(element: Union[WKBElement, WKTElement]):
|
|
48
48
|
lake = Session.query(Lake).get(1)
|
49
49
|
polygon = to_shape(lake.geom)
|
50
50
|
"""
|
51
|
-
assert isinstance(element, (WKBElement, WKTElement))
|
52
51
|
if isinstance(element, WKBElement):
|
53
52
|
data, hex = (
|
54
53
|
(element.data, True) if isinstance(element.data, str) else (bytes(element.data), False)
|
geoalchemy2/types/__init__.py
CHANGED
@@ -40,7 +40,7 @@ def select_dialect(dialect_name):
|
|
40
40
|
known_dialects = {
|
41
41
|
"geopackage": dialects.geopackage,
|
42
42
|
"mysql": dialects.mysql,
|
43
|
-
"mariadb": dialects.
|
43
|
+
"mariadb": dialects.mariadb,
|
44
44
|
"postgresql": dialects.postgresql,
|
45
45
|
"sqlite": dialects.sqlite,
|
46
46
|
}
|
@@ -198,9 +198,9 @@ class _GISType(UserDefinedType):
|
|
198
198
|
return geometry_type, srid
|
199
199
|
|
200
200
|
|
201
|
-
@compiles(_GISType, "mariadb")
|
202
201
|
@compiles(_GISType, "mysql")
|
203
|
-
|
202
|
+
@compiles(_GISType, "mariadb")
|
203
|
+
def get_col_spec_mysql(self, compiler, *args, **kwargs):
|
204
204
|
if self.geometry_type is not None:
|
205
205
|
spec = "%s" % self.geometry_type
|
206
206
|
else:
|
@@ -208,7 +208,7 @@ def get_col_spec(self, *args, **kwargs):
|
|
208
208
|
|
209
209
|
if not self.nullable or self.spatial_index:
|
210
210
|
spec += " NOT NULL"
|
211
|
-
if self.srid > 0:
|
211
|
+
if self.srid > 0 and compiler.dialect.name != "mariadb":
|
212
212
|
spec += " SRID %d" % self.srid
|
213
213
|
return spec
|
214
214
|
|
@@ -245,7 +245,7 @@ class Geometry(_GISType):
|
|
245
245
|
""" The element class to use. Used by the parent class'
|
246
246
|
``result_processor`` method. """
|
247
247
|
|
248
|
-
cache_ok =
|
248
|
+
cache_ok = True
|
249
249
|
""" Disable cache for this type. """
|
250
250
|
|
251
251
|
|
@@ -275,7 +275,7 @@ class Geography(_GISType):
|
|
275
275
|
""" The element class to use. Used by the parent class'
|
276
276
|
``result_processor`` method. """
|
277
277
|
|
278
|
-
cache_ok =
|
278
|
+
cache_ok = True
|
279
279
|
""" Disable cache for this type. """
|
280
280
|
|
281
281
|
|
@@ -315,7 +315,7 @@ class Raster(_GISType):
|
|
315
315
|
""" The element class to use. Used by the parent class'
|
316
316
|
``result_processor`` method. """
|
317
317
|
|
318
|
-
cache_ok =
|
318
|
+
cache_ok = True
|
319
319
|
""" Disable cache for this type. """
|
320
320
|
|
321
321
|
def __init__(self, spatial_index=True, from_text=None, name=None, nullable=True) -> None:
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
from geoalchemy2.types.dialects import common # noqa
|
4
4
|
from geoalchemy2.types.dialects import geopackage # noqa
|
5
|
+
from geoalchemy2.types.dialects import mariadb # noqa
|
5
6
|
from geoalchemy2.types.dialects import mysql # noqa
|
6
7
|
from geoalchemy2.types.dialects import postgresql # noqa
|
7
8
|
from geoalchemy2.types.dialects import sqlite # noqa
|
@@ -0,0 +1,47 @@
|
|
1
|
+
"""This module defines specific functions for MySQL dialect."""
|
2
|
+
|
3
|
+
from geoalchemy2.elements import WKBElement
|
4
|
+
from geoalchemy2.elements import WKTElement
|
5
|
+
from geoalchemy2.elements import _SpatialElement
|
6
|
+
from geoalchemy2.exc import ArgumentError
|
7
|
+
from geoalchemy2.shape import to_shape
|
8
|
+
|
9
|
+
|
10
|
+
def bind_processor_process(spatial_type, bindvalue):
|
11
|
+
if isinstance(bindvalue, str):
|
12
|
+
wkt_match = WKTElement._REMOVE_SRID.match(bindvalue)
|
13
|
+
srid = wkt_match.group(2)
|
14
|
+
try:
|
15
|
+
if srid is not None:
|
16
|
+
srid = int(srid)
|
17
|
+
except (ValueError, TypeError): # pragma: no cover
|
18
|
+
raise ArgumentError(
|
19
|
+
f"The SRID ({srid}) of the supplied value can not be casted to integer"
|
20
|
+
)
|
21
|
+
|
22
|
+
if srid is not None and srid != spatial_type.srid:
|
23
|
+
raise ArgumentError(
|
24
|
+
f"The SRID ({srid}) of the supplied value is different "
|
25
|
+
f"from the one of the column ({spatial_type.srid})"
|
26
|
+
)
|
27
|
+
return wkt_match.group(3)
|
28
|
+
|
29
|
+
if (
|
30
|
+
isinstance(bindvalue, _SpatialElement)
|
31
|
+
and bindvalue.srid != -1
|
32
|
+
and bindvalue.srid != spatial_type.srid
|
33
|
+
):
|
34
|
+
raise ArgumentError(
|
35
|
+
f"The SRID ({bindvalue.srid}) of the supplied value is different "
|
36
|
+
f"from the one of the column ({spatial_type.srid})"
|
37
|
+
)
|
38
|
+
|
39
|
+
if isinstance(bindvalue, WKTElement):
|
40
|
+
bindvalue = bindvalue.as_wkt()
|
41
|
+
if bindvalue.srid <= 0:
|
42
|
+
bindvalue.srid = spatial_type.srid
|
43
|
+
return bindvalue
|
44
|
+
elif isinstance(bindvalue, WKBElement):
|
45
|
+
# With MariaDB we use Shapely to convert the WKBElement to an EWKT string
|
46
|
+
return to_shape(bindvalue).wkt
|
47
|
+
return bindvalue
|
@@ -1,31 +0,0 @@
|
|
1
|
-
geoalchemy2/__init__.py,sha256=Wb5f11AM_hYn56yrxOAOjCT3z76TCtseqjFo2hKsob4,1971
|
2
|
-
geoalchemy2/_functions.py,sha256=e8v584Fx_fmh8XnJvkuYEXrcyPDiWV95Mu2yw6L-LHY,62863
|
3
|
-
geoalchemy2/_functions_helpers.py,sha256=hVM9DDTgf-oM27EZu6ycW1y2ENnSxUJixdINetWsu0E,2651
|
4
|
-
geoalchemy2/alembic_helpers.py,sha256=WYjKiVVyHtlB4DI22z-G0-x8NnG8othAINUwmxBCMcs,27885
|
5
|
-
geoalchemy2/comparator.py,sha256=WUDXn10doDlJVnYiWbVIAhhBu7N5AO9BI8sNf2mhpA4,8100
|
6
|
-
geoalchemy2/elements.py,sha256=5yd_7dUQGbrLvgYvo6A2YYAFwTvP4_BskIdBCB0DrlM,13002
|
7
|
-
geoalchemy2/exc.py,sha256=Nn9bRKB_35skWDMkEf4_Y2GL6gvguPVycPZcbOfa69g,226
|
8
|
-
geoalchemy2/functions.py,sha256=0he8hy_SAWpXAFPdfg32NMW5G0FaZP7yVl0jn0MRLBQ,10320
|
9
|
-
geoalchemy2/functions.pyi,sha256=rKCKdDSVTgeUQHItkOKqJSEuyKmVJ30bpjLpCPwMI5g,108636
|
10
|
-
geoalchemy2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
-
geoalchemy2/shape.py,sha256=TZJIhCN4p4ZVwZMXidxYxTMeeyReAyoFzyYwspJRqSA,2792
|
12
|
-
geoalchemy2/utils.py,sha256=OYWYnT64tjp4DWhPdq3KIxHbVti932xPMtGGxajtu-I,488
|
13
|
-
geoalchemy2/admin/__init__.py,sha256=s8L5C9pYC5dTxhIp9j-JzWeg9kEJevXUW8Pq4_L4aZ8,3984
|
14
|
-
geoalchemy2/admin/dialects/__init__.py,sha256=XodzuBbWKkkSQzM5EL3I33azuE-y_go0nVOlmIyJ13g,367
|
15
|
-
geoalchemy2/admin/dialects/common.py,sha256=8OkGa7T2Gxz2vDVnC9nva8Ma0YqBwGxHWfBJoZF1cNQ,2936
|
16
|
-
geoalchemy2/admin/dialects/geopackage.py,sha256=UaJignKtlo13oHxq92yescVJtXFEXkm4fy5WzR6sLT8,13469
|
17
|
-
geoalchemy2/admin/dialects/mysql.py,sha256=z2gmkTGav60_myDCFxO1yeezDKBcNcMxFvcpH-OqVKw,6867
|
18
|
-
geoalchemy2/admin/dialects/postgresql.py,sha256=VwB_h3TC8M5aQ6aKE3UYgxHfbEKav3eIHJeLx534Zzg,6191
|
19
|
-
geoalchemy2/admin/dialects/sqlite.py,sha256=UYQuNDDy-1-lsvYlqyKy01UtHgLF4iEN42fE5D-9QQk,13597
|
20
|
-
geoalchemy2/types/__init__.py,sha256=dD2-E8rr1nXr_d4CqyiEvRjO1H-dDRQnzr5B1g3_an8,14186
|
21
|
-
geoalchemy2/types/dialects/__init__.py,sha256=sw__RqGAFVrUPGrICjsva9SPoYLBNfyAkBHmsJkT7k0,359
|
22
|
-
geoalchemy2/types/dialects/common.py,sha256=gxKaRQhIODJhu_yBurlvFNAqGh3LfPNu_DcObEG1wlU,138
|
23
|
-
geoalchemy2/types/dialects/geopackage.py,sha256=nRmN_PnF-CWMEHkWhKVdsybnw3SvXZIBXAHIHXJLTqw,147
|
24
|
-
geoalchemy2/types/dialects/mysql.py,sha256=zMNi1920v0XlVaCJWSwtq0b0P5YQRn8y3X_rBxC5KEw,1790
|
25
|
-
geoalchemy2/types/dialects/postgresql.py,sha256=7NBKEbDJXMwX8Sgs6o_N2bAUHgjUcjbrdmYOA7sDiRw,1117
|
26
|
-
geoalchemy2/types/dialects/sqlite.py,sha256=B-yLzaQcqL_4dXoOPX9D9IXw2ZQlq-Tibv_kqUIBeb4,2104
|
27
|
-
GeoAlchemy2-0.15.2.dist-info/COPYING.rst,sha256=-bQKftq9uMOROzF7oN65kYBBIJKxTmBuDoftp27IC3I,1056
|
28
|
-
GeoAlchemy2-0.15.2.dist-info/METADATA,sha256=7ti009u_zE-vAPIyGyfVcC-oZzloZ-TYOKOV7ZahmKo,2087
|
29
|
-
GeoAlchemy2-0.15.2.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
|
30
|
-
GeoAlchemy2-0.15.2.dist-info/top_level.txt,sha256=3kGUTcfBeXd61zFpof6-qiuw1peNF_HuZabgHQgrdis,12
|
31
|
-
GeoAlchemy2-0.15.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|