GeoAlchemy2 0.16.0__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.16.0.dist-info → GeoAlchemy2-0.17.0.dist-info}/METADATA +14 -2
- {GeoAlchemy2-0.16.0.dist-info → GeoAlchemy2-0.17.0.dist-info}/RECORD +18 -15
- {GeoAlchemy2-0.16.0.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 +20 -72
- geoalchemy2/admin/dialects/sqlite.py +10 -5
- geoalchemy2/admin/plugin.py +98 -0
- geoalchemy2/shape.py +0 -1
- geoalchemy2/types/__init__.py +3 -3
- geoalchemy2/types/dialects/common.py +1 -1
- {GeoAlchemy2-0.16.0.dist-info → GeoAlchemy2-0.17.0.dist-info}/COPYING.rst +0 -0
- {GeoAlchemy2-0.16.0.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
|
@@ -28,6 +28,18 @@ Requires-Dist: SQLAlchemy>=1.4
|
|
28
28
|
Requires-Dist: packaging
|
29
29
|
Provides-Extra: shapely
|
30
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
|
@@ -1,4 +1,4 @@
|
|
1
|
-
geoalchemy2/__init__.py,sha256=
|
1
|
+
geoalchemy2/__init__.py,sha256=aGo3WcjOBcqrvqYXBuzCff1d_WiFUFObgtcyR11gg6M,2043
|
2
2
|
geoalchemy2/_functions.py,sha256=e8v584Fx_fmh8XnJvkuYEXrcyPDiWV95Mu2yw6L-LHY,62863
|
3
3
|
geoalchemy2/_functions_helpers.py,sha256=hVM9DDTgf-oM27EZu6ycW1y2ENnSxUJixdINetWsu0E,2651
|
4
4
|
geoalchemy2/alembic_helpers.py,sha256=WYjKiVVyHtlB4DI22z-G0-x8NnG8othAINUwmxBCMcs,27885
|
@@ -8,25 +8,28 @@ geoalchemy2/exc.py,sha256=Nn9bRKB_35skWDMkEf4_Y2GL6gvguPVycPZcbOfa69g,226
|
|
8
8
|
geoalchemy2/functions.py,sha256=0he8hy_SAWpXAFPdfg32NMW5G0FaZP7yVl0jn0MRLBQ,10320
|
9
9
|
geoalchemy2/functions.pyi,sha256=rKCKdDSVTgeUQHItkOKqJSEuyKmVJ30bpjLpCPwMI5g,108636
|
10
10
|
geoalchemy2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
-
geoalchemy2/shape.py,sha256=
|
11
|
+
geoalchemy2/shape.py,sha256=TLeWa6NwXZqBxh3Zj-l96wBB5kPviEhnZ44kyr8v678,2735
|
12
12
|
geoalchemy2/utils.py,sha256=OYWYnT64tjp4DWhPdq3KIxHbVti932xPMtGGxajtu-I,488
|
13
|
-
geoalchemy2/admin/__init__.py,sha256=
|
14
|
-
geoalchemy2/admin/
|
15
|
-
geoalchemy2/admin/dialects/
|
16
|
-
geoalchemy2/admin/dialects/
|
17
|
-
geoalchemy2/admin/dialects/
|
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
|
18
20
|
geoalchemy2/admin/dialects/postgresql.py,sha256=VwB_h3TC8M5aQ6aKE3UYgxHfbEKav3eIHJeLx534Zzg,6191
|
19
|
-
geoalchemy2/admin/dialects/sqlite.py,sha256=
|
20
|
-
geoalchemy2/types/__init__.py,sha256=
|
21
|
+
geoalchemy2/admin/dialects/sqlite.py,sha256=iW5MBhbxVKGMVghN5Do0iF-_9Pbtrv65NTXmW9mOyZA,13802
|
22
|
+
geoalchemy2/types/__init__.py,sha256=Ud1_6gukAs3gSzN3BThZnC-p0naHnTcNEjlyD5Ahm-M,14240
|
21
23
|
geoalchemy2/types/dialects/__init__.py,sha256=GYqO6nDtzElvsV22VN1SSUet2elaZG4ZuRrM-asbstM,414
|
22
|
-
geoalchemy2/types/dialects/common.py,sha256=
|
24
|
+
geoalchemy2/types/dialects/common.py,sha256=eiIKe-sFAdzgvQ7YT0cV29tYAPrs55GXKJi3p1UvjPQ,158
|
23
25
|
geoalchemy2/types/dialects/geopackage.py,sha256=nRmN_PnF-CWMEHkWhKVdsybnw3SvXZIBXAHIHXJLTqw,147
|
24
26
|
geoalchemy2/types/dialects/mariadb.py,sha256=tPZpfj95XlVSOm8zC52iFbqVrIOdj-EDH5KVJP1Gd9c,1723
|
25
27
|
geoalchemy2/types/dialects/mysql.py,sha256=zMNi1920v0XlVaCJWSwtq0b0P5YQRn8y3X_rBxC5KEw,1790
|
26
28
|
geoalchemy2/types/dialects/postgresql.py,sha256=7NBKEbDJXMwX8Sgs6o_N2bAUHgjUcjbrdmYOA7sDiRw,1117
|
27
29
|
geoalchemy2/types/dialects/sqlite.py,sha256=B-yLzaQcqL_4dXoOPX9D9IXw2ZQlq-Tibv_kqUIBeb4,2104
|
28
|
-
GeoAlchemy2-0.
|
29
|
-
GeoAlchemy2-0.
|
30
|
-
GeoAlchemy2-0.
|
31
|
-
GeoAlchemy2-0.
|
32
|
-
GeoAlchemy2-0.
|
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)
|
@@ -8,9 +8,6 @@ from geoalchemy2 import functions
|
|
8
8
|
from geoalchemy2.admin.dialects.common import _check_spatial_type
|
9
9
|
from geoalchemy2.admin.dialects.common import _spatial_idx_name
|
10
10
|
from geoalchemy2.admin.dialects.common import setup_create_drop
|
11
|
-
from geoalchemy2.elements import WKBElement
|
12
|
-
from geoalchemy2.elements import WKTElement
|
13
|
-
from geoalchemy2.shape import to_shape
|
14
11
|
from geoalchemy2.types import Geography
|
15
12
|
from geoalchemy2.types import Geometry
|
16
13
|
|
@@ -51,7 +48,7 @@ def reflect_geometry_column(inspector, table, column_info):
|
|
51
48
|
is_nullable = str(nullable_str).lower() == "yes"
|
52
49
|
|
53
50
|
if geometry_type not in _POSSIBLE_TYPES:
|
54
|
-
return
|
51
|
+
return # pragma: no cover
|
55
52
|
|
56
53
|
# Check if the column has spatial index
|
57
54
|
has_index_query = """SELECT DISTINCT
|
@@ -75,6 +72,25 @@ def reflect_geometry_column(inspector, table, column_info):
|
|
75
72
|
)
|
76
73
|
|
77
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
|
+
|
78
94
|
def before_create(table, bind, **kw):
|
79
95
|
"""Handle spatial indexes during the before_create event."""
|
80
96
|
dialect, gis_cols, regular_cols = setup_create_drop(table, bind)
|
@@ -137,7 +153,6 @@ def _compiles_mysql(cls, fn):
|
|
137
153
|
return "{}({})".format(fn, compiler.process(element.clauses, **kw))
|
138
154
|
|
139
155
|
compiles(getattr(functions, cls), "mysql")(_compile_mysql)
|
140
|
-
compiles(getattr(functions, cls), "mariadb")(_compile_mysql)
|
141
156
|
|
142
157
|
|
143
158
|
def register_mysql_mapping(mapping):
|
@@ -172,9 +187,6 @@ def _compile_GeomFromText_MySql(element, compiler, **kw):
|
|
172
187
|
|
173
188
|
def _compile_GeomFromWKB_MySql(element, compiler, **kw):
|
174
189
|
element.identifier = "ST_GeomFromWKB"
|
175
|
-
wkb_data = list(element.clauses)[0].value
|
176
|
-
if isinstance(wkb_data, memoryview):
|
177
|
-
list(element.clauses)[0].value = wkb_data.tobytes()
|
178
190
|
compiled = compiler.process(element.clauses, **kw)
|
179
191
|
srid = element.type.srid
|
180
192
|
|
@@ -184,50 +196,6 @@ def _compile_GeomFromWKB_MySql(element, compiler, **kw):
|
|
184
196
|
return "{}({})".format(element.identifier, compiled)
|
185
197
|
|
186
198
|
|
187
|
-
def _compile_GeomFromText_MariaDB(element, compiler, **kw):
|
188
|
-
element.identifier = "ST_GeomFromText"
|
189
|
-
compiled = compiler.process(element.clauses, **kw)
|
190
|
-
try:
|
191
|
-
clauses = list(element.clauses)
|
192
|
-
data_element = WKTElement(clauses[0].value)
|
193
|
-
srid = max(0, data_element.srid)
|
194
|
-
if srid <= 0:
|
195
|
-
srid = max(0, element.type.srid)
|
196
|
-
if len(clauses) > 1 and srid > 0:
|
197
|
-
clauses[1].value = srid
|
198
|
-
except Exception:
|
199
|
-
srid = max(0, element.type.srid)
|
200
|
-
|
201
|
-
if srid > 0:
|
202
|
-
res = "{}({}, {})".format(element.identifier, compiled, srid)
|
203
|
-
else:
|
204
|
-
res = "{}({})".format(element.identifier, compiled)
|
205
|
-
return res
|
206
|
-
|
207
|
-
|
208
|
-
def _compile_GeomFromWKB_MariaDB(element, compiler, **kw):
|
209
|
-
element.identifier = "ST_GeomFromText"
|
210
|
-
|
211
|
-
try:
|
212
|
-
clauses = list(element.clauses)
|
213
|
-
data_element = WKBElement(clauses[0].value)
|
214
|
-
srid = max(0, data_element.srid)
|
215
|
-
if srid <= 0:
|
216
|
-
srid = max(0, element.type.srid)
|
217
|
-
clauses[0].value = to_shape(data_element).wkt.encode("utf-8")
|
218
|
-
if len(clauses) > 1 and srid > 0:
|
219
|
-
clauses[1].value = srid
|
220
|
-
except Exception:
|
221
|
-
srid = max(0, element.type.srid)
|
222
|
-
compiled = compiler.process(element.clauses, **kw)
|
223
|
-
|
224
|
-
if srid > 0:
|
225
|
-
res = "{}({}, {})".format(element.identifier, compiled, srid)
|
226
|
-
else:
|
227
|
-
res = "{}({})".format(element.identifier, compiled)
|
228
|
-
return res
|
229
|
-
|
230
|
-
|
231
199
|
@compiles(functions.ST_GeomFromText, "mysql") # type: ignore
|
232
200
|
def _MySQL_ST_GeomFromText(element, compiler, **kw):
|
233
201
|
return _compile_GeomFromText_MySql(element, compiler, **kw)
|
@@ -238,16 +206,6 @@ def _MySQL_ST_GeomFromEWKT(element, compiler, **kw):
|
|
238
206
|
return _compile_GeomFromText_MySql(element, compiler, **kw)
|
239
207
|
|
240
208
|
|
241
|
-
@compiles(functions.ST_GeomFromText, "mariadb") # type: ignore
|
242
|
-
def _MariaDB_ST_GeomFromText(element, compiler, **kw):
|
243
|
-
return _compile_GeomFromText_MariaDB(element, compiler, **kw)
|
244
|
-
|
245
|
-
|
246
|
-
@compiles(functions.ST_GeomFromEWKT, "mariadb") # type: ignore
|
247
|
-
def _MariaDB_ST_GeomFromEWKT(element, compiler, **kw):
|
248
|
-
return _compile_GeomFromText_MariaDB(element, compiler, **kw)
|
249
|
-
|
250
|
-
|
251
209
|
@compiles(functions.ST_GeomFromWKB, "mysql") # type: ignore
|
252
210
|
def _MySQL_ST_GeomFromWKB(element, compiler, **kw):
|
253
211
|
return _compile_GeomFromWKB_MySql(element, compiler, **kw)
|
@@ -256,13 +214,3 @@ def _MySQL_ST_GeomFromWKB(element, compiler, **kw):
|
|
256
214
|
@compiles(functions.ST_GeomFromEWKB, "mysql") # type: ignore
|
257
215
|
def _MySQL_ST_GeomFromEWKB(element, compiler, **kw):
|
258
216
|
return _compile_GeomFromWKB_MySql(element, compiler, **kw)
|
259
|
-
|
260
|
-
|
261
|
-
@compiles(functions.ST_GeomFromWKB, "mariadb") # type: ignore
|
262
|
-
def _MariaDB_ST_GeomFromWKB(element, compiler, **kw):
|
263
|
-
return _compile_GeomFromWKB_MariaDB(element, compiler, **kw)
|
264
|
-
|
265
|
-
|
266
|
-
@compiles(functions.ST_GeomFromEWKB, "mariadb") # type: ignore
|
267
|
-
def _MariaDB_ST_GeomFromEWKB(element, compiler, **kw):
|
268
|
-
return _compile_GeomFromWKB_MariaDB(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
@@ -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:
|
File without changes
|
File without changes
|