GeoAlchemy2 0.14.7__tar.gz → 0.15.0__tar.gz
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.14.7 → geoalchemy2-0.15.0}/.github/workflows/test_and_publish.yml +8 -2
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/.pre-commit-config.yaml +3 -3
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/CHANGES.txt +6 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0/GeoAlchemy2.egg-info}/PKG-INFO +1 -1
- {GeoAlchemy2-0.14.7/GeoAlchemy2.egg-info → geoalchemy2-0.15.0}/PKG-INFO +1 -1
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/conf.py +3 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/spatialite_tutorial.rst +1 -1
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/__init__.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/_functions_helpers.py +2 -2
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/admin/__init__.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/admin/dialects/__init__.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/admin/dialects/common.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/admin/dialects/geopackage.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/admin/dialects/mysql.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/admin/dialects/postgresql.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/admin/dialects/sqlite.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/alembic_helpers.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/comparator.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/elements.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/functions.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/functions.pyi +2 -2
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/shape.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/types/__init__.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/types/dialects/__init__.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/types/dialects/geopackage.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/types/dialects/mysql.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/types/dialects/postgresql.py +1 -0
- geoalchemy2-0.15.0/geoalchemy2/types/dialects/sqlite.py +50 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/test_container/Dockerfile +1 -1
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/test_container/helpers/install_requirements.sh +6 -4
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/gallery/test_decipher_raster.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/gallery/test_disable_wrapping.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/gallery/test_insert_raster.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/gallery/test_length_at_insert.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/gallery/test_orm_mapped_v2.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/gallery/test_raster_transform.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/gallery/test_specific_compilation.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/gallery/test_summarystatsagg.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/gallery/test_type_decorator.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/schema_fixtures.py +2 -1
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/test_alembic_migrations.py +1 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/test_functional.py +135 -1
- GeoAlchemy2-0.14.7/geoalchemy2/types/dialects/sqlite.py +0 -21
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/.codespellrc +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/.coveragerc +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/.flake8 +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/.github/ISSUE_TEMPLATE/how_to_use.yaml +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/.github/dependabot.yml +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/.github/pull_request_template.md +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/.gitignore +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/.readthedocs.yaml +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/COPYING.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/GeoAlchemy2.egg-info/SOURCES.txt +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/GeoAlchemy2.egg-info/dependency_links.txt +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/GeoAlchemy2.egg-info/not-zip-safe +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/GeoAlchemy2.egg-info/requires.txt +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/GeoAlchemy2.egg-info/top_level.txt +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/GeoAlchemy2_dev.yml +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/MANIFEST.in +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/README.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/RELEASE.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/TEST.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/Makefile +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_static/geoalchemy.png +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_static/geoalchemy.svg +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_static/geoalchemy_small.png +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_static/geoalchemy_small.svg +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_templates/sidebar-about.html +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_templates/sidebar-links.html +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_templates/sidebar-logo.html +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_themes/LICENSE +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_themes/README +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_themes/flask/layout.html +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_themes/flask/relations.html +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_themes/flask/static/flasky.css_t +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_themes/flask/static/small_flask.css +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/_themes/flask/theme.conf +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/admin.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/alembic.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/alembic_helpers.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/changelog.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/core_tutorial.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/elements.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/index.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/make.bat +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/migrate.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/orm_tutorial.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/shape.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/spatial_functions.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/spatial_operators.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/doc/types.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/generate_type_stubs.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/_functions.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/exc.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/py.typed +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/types/dialects/common.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/geoalchemy2/utils.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/pyproject.toml +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/requirements-doc.txt +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/requirements-mypy.txt +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/requirements-rtd.txt +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/requirements.txt +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/setup.cfg +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/setup.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/test_container/.dockerignore +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/test_container/.gitignore +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/test_container/build.sh +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/test_container/helpers/entrypoint.sh +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/test_container/helpers/init_mysql.sh +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/test_container/helpers/init_postgres.sh +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/test_container/run.sh +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/__init__.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/alembic_config/alembic.ini +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/conftest.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/data/spatialite_ge_4.sqlite +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/data/spatialite_geopackage.gpkg +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/data/spatialite_lt_4.sqlite +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/gallery/README.rst +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/gallery/__init__.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/test_comparator.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/test_elements.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/test_functional_geopackage.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/test_functional_mysql.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/test_functional_postgresql.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/test_functional_sqlite.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/test_functions.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/test_pickle.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/test_shape.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tests/test_types.py +0 -0
- {GeoAlchemy2-0.14.7 → geoalchemy2-0.15.0}/tox.ini +0 -0
@@ -41,7 +41,7 @@ jobs:
|
|
41
41
|
|
42
42
|
services:
|
43
43
|
postgres:
|
44
|
-
image:
|
44
|
+
image: postgis/postgis:16-3.4
|
45
45
|
env:
|
46
46
|
POSTGRES_DB: gis
|
47
47
|
POSTGRES_PASSWORD: gis
|
@@ -84,11 +84,17 @@ jobs:
|
|
84
84
|
psql -h localhost -p 5432 -U gis -d gis -c 'CREATE SCHEMA gis;'
|
85
85
|
|
86
86
|
# Add PostGIS extension to "gis" database
|
87
|
-
psql -h localhost -p 5432 -U gis -d gis -c 'CREATE EXTENSION IF NOT EXISTS postgis;'
|
87
|
+
psql -h localhost -p 5432 -U gis -d gis -c 'CREATE EXTENSION IF NOT EXISTS postgis SCHEMA public;'
|
88
88
|
|
89
89
|
# Drop PostGIS Tiger Geocoder extension to "gis" database
|
90
90
|
psql -h localhost -p 5432 -U gis -d gis -c 'DROP EXTENSION IF EXISTS postgis_tiger_geocoder CASCADE;'
|
91
91
|
|
92
|
+
# Add PostGISRaster extension to "gis" database
|
93
|
+
psql -h localhost -p 5432 -U gis -d gis -c 'CREATE EXTENSION IF NOT EXISTS postgis_raster SCHEMA public;'
|
94
|
+
|
95
|
+
# Drop PostGIS Topology extension to "gis" database
|
96
|
+
psql -h localhost -p 5432 -U gis -d gis -c 'DROP EXTENSION IF EXISTS postgis_topology;'
|
97
|
+
|
92
98
|
# Setup MySQL
|
93
99
|
- name: Set up MySQL
|
94
100
|
run: |
|
@@ -13,11 +13,11 @@ repos:
|
|
13
13
|
- id: end-of-file-fixer
|
14
14
|
- id: trailing-whitespace
|
15
15
|
- repo: https://github.com/pycqa/isort
|
16
|
-
rev: 5.
|
16
|
+
rev: 5.13.2
|
17
17
|
hooks:
|
18
18
|
- id: isort
|
19
19
|
- repo: https://github.com/psf/black
|
20
|
-
rev:
|
20
|
+
rev: 24.3.0
|
21
21
|
hooks:
|
22
22
|
- id: black
|
23
23
|
- repo: https://github.com/codespell-project/codespell
|
@@ -31,7 +31,7 @@ repos:
|
|
31
31
|
additional_dependencies: ["tomli"]
|
32
32
|
exclude: "tests"
|
33
33
|
- repo: https://github.com/PyCQA/flake8
|
34
|
-
rev:
|
34
|
+
rev: 7.0.0
|
35
35
|
hooks:
|
36
36
|
- id: flake8
|
37
37
|
ci:
|
@@ -60,6 +60,9 @@ copyright = "2012, Eric Lemoine"
|
|
60
60
|
#
|
61
61
|
version = release = geoalchemy2.__version__
|
62
62
|
|
63
|
+
# Remove some Sphinx warnings
|
64
|
+
suppress_warnings = ["config.cache"]
|
65
|
+
|
63
66
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
64
67
|
# for a list of supported languages.
|
65
68
|
# language = None
|
@@ -54,7 +54,7 @@ Declare a Mapping
|
|
54
54
|
Now that we have a working connection we can go ahead and create a mapping between
|
55
55
|
a Python class and a database table::
|
56
56
|
|
57
|
-
>>> from sqlalchemy.
|
57
|
+
>>> from sqlalchemy.orm import declarative_base
|
58
58
|
>>> from sqlalchemy import Column, Integer, String
|
59
59
|
>>> from geoalchemy2 import Geometry
|
60
60
|
>>>
|
@@ -60,8 +60,8 @@ class TableRowElement(ColumnElement):
|
|
60
60
|
"""The cache is disabled for this class."""
|
61
61
|
|
62
62
|
def __init__(self, selectable: bool) -> None: ...
|
63
|
-
@property
|
64
|
-
def _from_objects(self) -> List[bool]: ...
|
63
|
+
@property # type: ignore[override]
|
64
|
+
def _from_objects(self) -> List[bool]: ...
|
65
65
|
'''
|
66
66
|
stub_file_parts = [header]
|
67
67
|
|
@@ -117,6 +117,7 @@ class WKTElement(_SpatialElement):
|
|
117
117
|
"""
|
118
118
|
|
119
119
|
_REMOVE_SRID = re.compile("(SRID=([0-9]+); ?)?(.*)")
|
120
|
+
SPLIT_WKT_PATTERN = re.compile(r"((SRID=\d+) *; *)?([\w ]+) *(\([\d ,\(\)]+\))")
|
120
121
|
|
121
122
|
geom_from: str = "ST_GeomFromText"
|
122
123
|
geom_from_extended_version: str = "ST_GeomFromEWKT"
|
@@ -13,8 +13,8 @@ class TableRowElement(ColumnElement):
|
|
13
13
|
"""The cache is disabled for this class."""
|
14
14
|
|
15
15
|
def __init__(self, selectable: bool) -> None: ...
|
16
|
-
@property
|
17
|
-
def _from_objects(self) -> List[bool]: ...
|
16
|
+
@property # type: ignore[override]
|
17
|
+
def _from_objects(self) -> List[bool]: ...
|
18
18
|
|
19
19
|
class ST_AsGeoJSON(GenericFunction):
|
20
20
|
"""
|
@@ -4,6 +4,7 @@ The :class:`geoalchemy2.types.Geometry`, :class:`geoalchemy2.types.Geography`, a
|
|
4
4
|
:class:`geoalchemy2.types.Raster` classes are used when defining geometry, geography and raster
|
5
5
|
columns/properties in models.
|
6
6
|
"""
|
7
|
+
|
7
8
|
import warnings
|
8
9
|
from typing import Any
|
9
10
|
from typing import Dict
|
@@ -0,0 +1,50 @@
|
|
1
|
+
"""This module defines specific functions for SQLite dialect."""
|
2
|
+
|
3
|
+
import re
|
4
|
+
|
5
|
+
from geoalchemy2.elements import RasterElement
|
6
|
+
from geoalchemy2.elements import WKBElement
|
7
|
+
from geoalchemy2.elements import WKTElement
|
8
|
+
from geoalchemy2.shape import to_shape
|
9
|
+
|
10
|
+
|
11
|
+
def format_geom_type(wkt, default_srid=None):
|
12
|
+
"""Format the Geometry type for SQLite."""
|
13
|
+
match = re.match(WKTElement.SPLIT_WKT_PATTERN, wkt)
|
14
|
+
if match is None:
|
15
|
+
return wkt
|
16
|
+
_, srid, geom_type, coords = match.groups()
|
17
|
+
geom_type = geom_type.replace(" ", "")
|
18
|
+
if geom_type.endswith("ZM"):
|
19
|
+
geom_type = geom_type[:-2]
|
20
|
+
elif geom_type.endswith("Z"):
|
21
|
+
geom_type = geom_type[:-1]
|
22
|
+
if srid is None and default_srid is not None:
|
23
|
+
srid = f"SRID={default_srid}"
|
24
|
+
if srid is not None:
|
25
|
+
return "%s;%s%s" % (srid, geom_type, coords)
|
26
|
+
else:
|
27
|
+
return "%s%s" % (geom_type, coords)
|
28
|
+
|
29
|
+
|
30
|
+
def bind_processor_process(spatial_type, bindvalue):
|
31
|
+
if isinstance(bindvalue, WKTElement):
|
32
|
+
return format_geom_type(
|
33
|
+
bindvalue.data,
|
34
|
+
default_srid=bindvalue.srid if bindvalue.srid >= 0 else spatial_type.srid,
|
35
|
+
)
|
36
|
+
elif isinstance(bindvalue, WKBElement):
|
37
|
+
# With SpatiaLite we use Shapely to convert the WKBElement to an EWKT string
|
38
|
+
shape = to_shape(bindvalue)
|
39
|
+
# shapely.wkb.loads returns geom_type with a 'Z', for example, 'LINESTRING Z'
|
40
|
+
# which is a limitation with SpatiaLite. Hence, a temporary fix.
|
41
|
+
res = format_geom_type(
|
42
|
+
shape.wkt, default_srid=bindvalue.srid if bindvalue.srid >= 0 else spatial_type.srid
|
43
|
+
)
|
44
|
+
return res
|
45
|
+
elif isinstance(bindvalue, RasterElement):
|
46
|
+
return "%s" % (bindvalue.data)
|
47
|
+
elif isinstance(bindvalue, str):
|
48
|
+
return format_geom_type(bindvalue, default_srid=spatial_type.srid)
|
49
|
+
else:
|
50
|
+
return bindvalue
|
@@ -5,7 +5,7 @@ RUN /install_requirements.sh
|
|
5
5
|
|
6
6
|
COPY ./helpers/init_postgres.sh /
|
7
7
|
env PGDATA="/var/lib/postgresql/data"
|
8
|
-
env POSTGRES_PATH="/usr/lib/postgresql/
|
8
|
+
env POSTGRES_PATH="/usr/lib/postgresql/16"
|
9
9
|
RUN su postgres -c /init_postgres.sh
|
10
10
|
|
11
11
|
ENV SPATIALITE_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu/mod_spatialite.so"
|
@@ -29,9 +29,9 @@ packages=(
|
|
29
29
|
python3.12-venv
|
30
30
|
|
31
31
|
# PostgreSQL and PostGIS
|
32
|
-
postgresql
|
33
|
-
postgresql-
|
34
|
-
postgresql-
|
32
|
+
postgresql-16
|
33
|
+
postgresql-16-postgis-3
|
34
|
+
postgresql-16-postgis-3-scripts
|
35
35
|
libpq-dev
|
36
36
|
libgeos-dev
|
37
37
|
|
@@ -56,8 +56,10 @@ packages=(
|
|
56
56
|
export DEBIAN_FRONTEND=noninteractive
|
57
57
|
|
58
58
|
apt-get update -y
|
59
|
-
apt-get install --no-install-recommends -y software-properties-common gnupg2
|
59
|
+
apt-get install --no-install-recommends -y software-properties-common gnupg2 wget
|
60
60
|
add-apt-repository ppa:deadsnakes/ppa
|
61
|
+
sh -c 'echo "deb https://apt.PostgreSQL.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
|
62
|
+
wget --quiet -O - https://www.PostgreSQL.org/media/keys/ACCC4CF8.asc | apt-key add -
|
61
63
|
apt-get update -y
|
62
64
|
|
63
65
|
apt-get install --no-install-recommends -y "${packages[@]}"
|
@@ -7,6 +7,7 @@ the wrapping of geometry columns with a `ST_AsEWKB()` function might be annoying
|
|
7
7
|
this case it is possible to disable this wrapping.
|
8
8
|
This example uses SQLAlchemy ORM queries.
|
9
9
|
"""
|
10
|
+
|
10
11
|
from sqlalchemy import Column
|
11
12
|
from sqlalchemy import Integer
|
12
13
|
from sqlalchemy import func
|
@@ -6,6 +6,7 @@ New ORM Declarative Mapping Style
|
|
6
6
|
``sqlalchemy.orm.DeclarativeBase`` base class.
|
7
7
|
This example shows how to use GeoAlchemy2 types in this context.
|
8
8
|
"""
|
9
|
+
|
9
10
|
import pytest
|
10
11
|
from pkg_resources import parse_version
|
11
12
|
from sqlalchemy import __version__ as SA_VERSION
|
@@ -8,6 +8,7 @@ are projected in the DB. To avoid having to always tweak the query with a
|
|
8
8
|
``ST_Transform()``, it is possible to define a `TypeDecorator
|
9
9
|
<https://docs.sqlalchemy.org/en/13/core/custom_types.html#sqlalchemy.types.TypeDecorator>`_
|
10
10
|
"""
|
11
|
+
|
11
12
|
import re
|
12
13
|
from typing import Any
|
13
14
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Declare tables used in tests."""
|
2
|
+
|
2
3
|
import pytest
|
3
4
|
from sqlalchemy import Column
|
4
5
|
from sqlalchemy import Integer
|
@@ -119,7 +120,7 @@ class TransformedGeometry(TypeDecorator):
|
|
119
120
|
SRID of the resulting WKBElement is correct"""
|
120
121
|
return getattr(func, self.impl.as_binary)(
|
121
122
|
func.ST_Transform(col, self.app_srid),
|
122
|
-
type_=self.__class__.impl(srid=self.app_srid)
|
123
|
+
type_=self.__class__.impl(srid=self.app_srid),
|
123
124
|
# srid could also be -1 so that the SRID is deduced from the
|
124
125
|
# WKB data
|
125
126
|
)
|
@@ -275,6 +275,140 @@ class TestInsertionCore:
|
|
275
275
|
srid = conn.execute(row[1].ST_SRID()).scalar()
|
276
276
|
assert srid == 4326
|
277
277
|
|
278
|
+
@pytest.mark.parametrize(
|
279
|
+
"geom_type,wkt",
|
280
|
+
[
|
281
|
+
pytest.param("POINT", "(1 2)", id="Point"),
|
282
|
+
pytest.param("POINTZ", "(1 2 3)", id="Point Z"),
|
283
|
+
pytest.param("POINTM", "(1 2 3)", id="Point M"),
|
284
|
+
pytest.param("POINTZM", "(1 2 3 4)", id="Point ZM"),
|
285
|
+
pytest.param("LINESTRING", "(1 2, 3 4)", id="LineString"),
|
286
|
+
pytest.param("LINESTRINGZ", "(1 2 3, 4 5 6)", id="LineString Z"),
|
287
|
+
pytest.param("LINESTRINGM", "(1 2 3, 4 5 6)", id="LineString M"),
|
288
|
+
pytest.param("LINESTRINGZM", "(1 2 3 4, 5 6 7 8)", id="LineString ZM"),
|
289
|
+
pytest.param("POLYGON", "((1 2, 3 4, 5 6, 1 2))", id="Polygon"),
|
290
|
+
pytest.param("POLYGONZ", "((1 2 3, 4 5 6, 7 8 9, 1 2 3))", id="Polygon Z"),
|
291
|
+
pytest.param("POLYGONM", "((1 2 3, 4 5 6, 7 8 9, 1 2 3))", id="Polygon M"),
|
292
|
+
pytest.param(
|
293
|
+
"POLYGONZM", "((1 2 3 4, 5 6 7 8, 9 10 11 12, 1 2 3 4))", id="Polygon ZM"
|
294
|
+
),
|
295
|
+
pytest.param("MULTIPOINT", "(1 2, 3 4)", id="Multi Point"),
|
296
|
+
pytest.param("MULTIPOINTZ", "(1 2 3, 4 5 6)", id="Multi Point Z"),
|
297
|
+
pytest.param("MULTIPOINTM", "(1 2 3, 4 5 6)", id="Multi Point M"),
|
298
|
+
pytest.param("MULTIPOINTZM", "(1 2 3 4, 5 6 7 8)", id="Multi Point ZM"),
|
299
|
+
pytest.param("MULTILINESTRING", "((1 2, 3 4), (10 20, 30 40))", id="Multi LineString"),
|
300
|
+
pytest.param(
|
301
|
+
"MULTILINESTRINGZ",
|
302
|
+
"((1 2 3, 4 5 6), (10 20 30, 40 50 60))",
|
303
|
+
id="Multi LineString Z",
|
304
|
+
),
|
305
|
+
pytest.param(
|
306
|
+
"MULTILINESTRINGM",
|
307
|
+
"((1 2 3, 4 5 6), (10 20 30, 40 50 60))",
|
308
|
+
id="Multi LineString M",
|
309
|
+
),
|
310
|
+
pytest.param(
|
311
|
+
"MULTILINESTRINGZM",
|
312
|
+
"((1 2 3 4, 5 6 7 8), (10 20 30 40, 50 60 70 80))",
|
313
|
+
id="Multi LineString ZM",
|
314
|
+
),
|
315
|
+
pytest.param(
|
316
|
+
"MULTIPOLYGON",
|
317
|
+
"(((1 2, 3 4, 5 6, 1 2)), ((10 20, 30 40, 50 60, 10 20)))",
|
318
|
+
id="Multi Polygon",
|
319
|
+
),
|
320
|
+
pytest.param(
|
321
|
+
"MULTIPOLYGONZ",
|
322
|
+
"(((1 2 3, 4 5 6, 7 8 9, 1 2 3)), ((10 20 30, 40 50 60, 70 80 90, 10 20 30)))",
|
323
|
+
id="Multi Polygon Z",
|
324
|
+
),
|
325
|
+
pytest.param(
|
326
|
+
"MULTIPOLYGONM",
|
327
|
+
"(((1 2 3, 4 5 6, 7 8 9, 1 2 3)), ((10 20 30, 40 50 60, 70 80 90, 10 20 30)))",
|
328
|
+
id="Multi Polygon M",
|
329
|
+
),
|
330
|
+
pytest.param(
|
331
|
+
"MULTIPOLYGONZM",
|
332
|
+
"(((1 2 3 4, 5 6 7 8, 9 10 11 12, 1 2 3 4)),"
|
333
|
+
" ((10 20 30 40, 50 60 70 80, 90 100 100 120, 10 20 30 40)))",
|
334
|
+
id="Multi Polygon ZM",
|
335
|
+
),
|
336
|
+
],
|
337
|
+
)
|
338
|
+
def test_insert_all_geom_types(self, dialect_name, base, conn, metadata, geom_type, wkt):
|
339
|
+
"""Test insertion and selection of all geometry types."""
|
340
|
+
ndims = 2
|
341
|
+
if "Z" in geom_type[-2:]:
|
342
|
+
ndims += 1
|
343
|
+
if geom_type.endswith("M"):
|
344
|
+
ndims += 1
|
345
|
+
has_m = True
|
346
|
+
else:
|
347
|
+
has_m = False
|
348
|
+
|
349
|
+
if ndims > 2 and dialect_name == "mysql":
|
350
|
+
# Explicitly skip MySQL dialect to show that it can only work with 2D geometries
|
351
|
+
pytest.xfail(reason="MySQL only supports 2D geometry types")
|
352
|
+
|
353
|
+
class GeomTypeTable(base):
|
354
|
+
__tablename__ = "test_geom_types"
|
355
|
+
id = Column(Integer, primary_key=True)
|
356
|
+
geom = Column(Geometry(srid=4326, geometry_type=geom_type, dimension=ndims))
|
357
|
+
|
358
|
+
metadata.drop_all(bind=conn, checkfirst=True)
|
359
|
+
metadata.create_all(bind=conn)
|
360
|
+
|
361
|
+
inserted_wkt = f"{geom_type}{wkt}"
|
362
|
+
|
363
|
+
# Use the DB to generate the corresponding raw WKB
|
364
|
+
raw_wkb = conn.execute(
|
365
|
+
text("SELECT ST_AsBinary(ST_GeomFromText('{}', 4326))".format(inserted_wkt))
|
366
|
+
).scalar()
|
367
|
+
|
368
|
+
wkb_elem = WKBElement(raw_wkb, srid=4326)
|
369
|
+
inserted_elements = [
|
370
|
+
{"geom": inserted_wkt},
|
371
|
+
{"geom": f"SRID=4326;{inserted_wkt}"},
|
372
|
+
{"geom": WKTElement(inserted_wkt, srid=4326)},
|
373
|
+
{"geom": WKTElement(f"SRID=4326;{inserted_wkt}")},
|
374
|
+
]
|
375
|
+
if dialect_name not in ["postgresql", "sqlite"] or not has_m:
|
376
|
+
# Currently Shapely does not support geometry types with M dimension
|
377
|
+
inserted_elements.append({"geom": wkb_elem})
|
378
|
+
inserted_elements.append({"geom": wkb_elem.as_ewkb()})
|
379
|
+
|
380
|
+
# Insert the elements
|
381
|
+
conn.execute(
|
382
|
+
GeomTypeTable.__table__.insert(),
|
383
|
+
inserted_elements,
|
384
|
+
)
|
385
|
+
|
386
|
+
# Select the elements
|
387
|
+
query = select(
|
388
|
+
[
|
389
|
+
GeomTypeTable.__table__.c.id,
|
390
|
+
GeomTypeTable.__table__.c.geom.ST_AsText(),
|
391
|
+
GeomTypeTable.__table__.c.geom.ST_SRID(),
|
392
|
+
],
|
393
|
+
)
|
394
|
+
results = conn.execute(query)
|
395
|
+
rows = results.all()
|
396
|
+
|
397
|
+
# Check that the selected elements are the same as the inputs
|
398
|
+
for row_id, row, srid in rows:
|
399
|
+
checked_wkt = row.upper().replace(" ", "")
|
400
|
+
expected_wkt = inserted_wkt.upper().replace(" ", "")
|
401
|
+
if "MULTIPOINT" in geom_type:
|
402
|
+
# Some dialects return MULTIPOINT geometries with nested parenthesis and others
|
403
|
+
# do not so we remove them before checking the results
|
404
|
+
checked_wkt = re.sub(r"\(([0-9]+)\)", "\\1", checked_wkt)
|
405
|
+
if row_id >= 5 and dialect_name in ["geopackage"] and has_m:
|
406
|
+
# Currently Shapely does not support geometry types with M dimension
|
407
|
+
assert checked_wkt != expected_wkt
|
408
|
+
else:
|
409
|
+
assert checked_wkt == expected_wkt
|
410
|
+
assert srid == 4326
|
411
|
+
|
278
412
|
@test_only_with_dialects("postgresql", "sqlite")
|
279
413
|
def test_insert_geom_poi(self, conn, Poi, setup_tables):
|
280
414
|
conn.execute(
|
@@ -380,7 +514,7 @@ class TestInsertionORM:
|
|
380
514
|
lake = Lake("LINESTRING(0 0,1 1)")
|
381
515
|
session.add(lake)
|
382
516
|
|
383
|
-
if
|
517
|
+
if dialect_name == "postgresql" and postgis_version < 3:
|
384
518
|
with pytest.raises((DataError, IntegrityError)):
|
385
519
|
session.flush()
|
386
520
|
else:
|
@@ -1,21 +0,0 @@
|
|
1
|
-
"""This module defines specific functions for SQLite dialect."""
|
2
|
-
from geoalchemy2.elements import RasterElement
|
3
|
-
from geoalchemy2.elements import WKBElement
|
4
|
-
from geoalchemy2.elements import WKTElement
|
5
|
-
from geoalchemy2.shape import to_shape
|
6
|
-
|
7
|
-
|
8
|
-
def bind_processor_process(spatial_type, bindvalue):
|
9
|
-
if isinstance(bindvalue, WKTElement):
|
10
|
-
if bindvalue.extended:
|
11
|
-
return "%s" % (bindvalue.data)
|
12
|
-
else:
|
13
|
-
return "SRID=%d;%s" % (bindvalue.srid, bindvalue.data)
|
14
|
-
elif isinstance(bindvalue, WKBElement):
|
15
|
-
# With SpatiaLite we use Shapely to convert the WKBElement to an EWKT string
|
16
|
-
shape = to_shape(bindvalue)
|
17
|
-
return "SRID=%d;%s" % (bindvalue.srid, shape.wkt)
|
18
|
-
elif isinstance(bindvalue, RasterElement):
|
19
|
-
return "%s" % (bindvalue.data)
|
20
|
-
else:
|
21
|
-
return bindvalue
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|