sql functions reference lists https www gaiagis itgaiasinsspatialitesql4 3 0 html binary predicates returns true if b s bounding box completely contains a s bounding box returns true if a s bounding box completely contains b s bounding box returns true if a s bounding box overlaps b s bounding box these are implemented here as synonyms for equals distance predicates determine the version of the spatialite library try version self spatialiteversiontuple1 except exception as exc raise improperlyconfigured cannot determine the spatialite version for the s database was the spatialite initialization sql loaded on this database self connection settingsdictname from exc if version 4 3 0 raise improperlyconfiguredgeodjango supports spatialite 4 3 0 and above return version def convertextentself box if box is none return none shell geosgeometrybox shell xmin ymin shell0 2 xmax ymax shell2 2 return xmin ymin xmax ymax def geodbtypeself f return none def getdistanceself f value lookuptype if not value return value value0 if isinstancevalue distance if f geodeticself connection if lookuptype dwithin raise valueerror only numeric values of degree units are allowed on geographic dwithin queries distparam value m else distparam getattr value distance unitattnamef unitsnameself connection else distparam value return distparam def getspatialitefuncself func cursor self connection cursor try cursor executeselect s func row cursor fetchone finally cursor close return row0 def geosversionself return the version of geos used by spatialite as a string return self getspatialitefuncgeosversion def projversionself return the version of lwgeom library used by spatialite return self getspatialitefunclwgeomversion def rttopoversionself return the version of the versiondependant geom library used by spatialite return the spatialite version as a tuple version string major minor subminor return the spatial aggregate sql template and function for the given aggregate instance routines for getting the ogccompliant models binary predicates returns true if b s bounding box completely contains a s bounding box returns true if a s bounding box completely contains b s bounding box returns true if a s bounding box overlaps b s bounding box these are implemented here as synonyms for equals distance predicates determine the version of the spatialite library convert the polygon data received from spatialite to min max values return none because geometry columns are added via the addgeometrycolumn stored procedure on spatialite return the distance parameters for the given geometry field lookup value and lookup type helper routine for calling spatialite functions and returning their result any error occurring in this method should be handled by the caller return the version of the proj library used by spatialite return the version of lwgeom library used by spatialite return the version of rttopo library used by spatialite return the version of the version dependant geom library used by spatialite return the spatialite version as a tuple version string major minor subminor return the spatial aggregate sql template and function for the given aggregate instance routines for getting the ogc compliant models
from django.contrib.gis.db import models
from django.contrib.gis.db.backends.base.operations import BaseSpatialOperations
from django.contrib.gis.db.backends.spatialite.adapter import SpatiaLiteAdapter
from django.contrib.gis.db.backends.utils import SpatialOperator
from django.contrib.gis.geos.geometry import GEOSGeometry, GEOSGeometryBase
from django.contrib.gis.geos.prototypes.io import wkb_r
from django.contrib.gis.measure import Distance
from django.core.exceptions import ImproperlyConfigured
from django.db.backends.sqlite3.operations import DatabaseOperations
from django.utils.functional import cached_property
from django.utils.version import get_version_tuple


class SpatialiteNullCheckOperator(SpatialOperator):
    def as_sql(self, connection, lookup, template_params, sql_params):
        sql, params = super().as_sql(connection, lookup, template_params, sql_params)
        return "%s > 0" % sql, params


class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
    name = "spatialite"
    spatialite = True

    Adapter = SpatiaLiteAdapter

    collect = "Collect"
    extent = "Extent"
    makeline = "MakeLine"
    unionagg = "GUnion"

    from_text = "GeomFromText"

    gis_operators = {
        
        "equals": SpatialiteNullCheckOperator(func="Equals"),
        "disjoint": SpatialiteNullCheckOperator(func="Disjoint"),
        "touches": SpatialiteNullCheckOperator(func="Touches"),
        "crosses": SpatialiteNullCheckOperator(func="Crosses"),
        "within": SpatialiteNullCheckOperator(func="Within"),
        "overlaps": SpatialiteNullCheckOperator(func="Overlaps"),
        "contains": SpatialiteNullCheckOperator(func="Contains"),
        "intersects": SpatialiteNullCheckOperator(func="Intersects"),
        "relate": SpatialiteNullCheckOperator(func="Relate"),
        "coveredby": SpatialiteNullCheckOperator(func="CoveredBy"),
        "covers": SpatialiteNullCheckOperator(func="Covers"),
        
        "contained": SpatialOperator(func="MbrWithin"),
        
        "bbcontains": SpatialOperator(func="MbrContains"),
        
        "bboverlaps": SpatialOperator(func="MbrOverlaps"),
        
        "same_as": SpatialiteNullCheckOperator(func="Equals"),
        "exact": SpatialiteNullCheckOperator(func="Equals"),
        
        "dwithin": SpatialOperator(func="PtDistWithin"),
    }

    disallowed_aggregates = (models.Extent3D,)

    select = "CAST (AsEWKB(%s) AS BLOB)"

    function_names = {
        "AsWKB": "St_AsBinary",
        "ForcePolygonCW": "ST_ForceLHR",
        "FromWKB": "ST_GeomFromWKB",
        "FromWKT": "ST_GeomFromText",
        "Length": "ST_Length",
        "LineLocatePoint": "ST_Line_Locate_Point",
        "NumPoints": "ST_NPoints",
        "Reverse": "ST_Reverse",
        "Scale": "ScaleCoords",
        "Translate": "ST_Translate",
        "Union": "ST_Union",
    }

    @cached_property
    def unsupported_functions(self):
        unsupported = {"BoundingCircle", "GeometryDistance", "IsEmpty", "MemSize"}
        if not self.geom_lib_version():
            unsupported |= {"Azimuth", "GeoHash", "MakeValid"}
        return unsupported

    @cached_property
    def spatial_version(self):
        
        try:
            version = self.spatialite_version_tuple()[1:]
        except Exception as exc:
            raise ImproperlyConfigured(
                'Cannot determine the SpatiaLite version for the "%s" database. '
                "Was the SpatiaLite initialization SQL loaded on this database?"
                % (self.connection.settings_dict["NAME"],)
            ) from exc
        if version < (4, 3, 0):
            raise ImproperlyConfigured("GeoDjango supports SpatiaLite 4.3.0 and above.")
        return version

    def convert_extent(self, box):
        
        if box is None:
            return None
        shell = GEOSGeometry(box).shell
        xmin, ymin = shell[0][:2]
        xmax, ymax = shell[2][:2]
        return (xmin, ymin, xmax, ymax)

    def geo_db_type(self, f):
        
        return None

    def get_distance(self, f, value, lookup_type):
        
        if not value:
            return []
        value = value[0]
        if isinstance(value, Distance):
            if f.geodetic(self.connection):
                if lookup_type == "dwithin":
                    raise ValueError(
                        "Only numeric values of degree units are allowed on "
                        "geographic DWithin queries."
                    )
                dist_param = value.m
            else:
                dist_param = getattr(
                    value, Distance.unit_attname(f.units_name(self.connection))
                )
        else:
            dist_param = value
        return [dist_param]

    def _get_spatialite_func(self, func):
        
        cursor = self.connection._cursor()
        try:
            cursor.execute("SELECT %s" % func)
            row = cursor.fetchone()
        finally:
            cursor.close()
        return row[0]

    def geos_version(self):
        "Return the version of GEOS used by SpatiaLite as a string."
        return self._get_spatialite_func("geos_version()")

    def proj_version(self):
        
        return self._get_spatialite_func("proj4_version()")

    def lwgeom_version(self):
        
        return self._get_spatialite_func("lwgeom_version()")

    def rttopo_version(self):
        
        return self._get_spatialite_func("rttopo_version()")

    def geom_lib_version(self):
        
        if self.spatial_version >= (5,):
            return self.rttopo_version()
        else:
            return self.lwgeom_version()

    def spatialite_version(self):
        "Return the SpatiaLite library version as a string."
        return self._get_spatialite_func("spatialite_version()")

    def spatialite_version_tuple(self):
        
        version = self.spatialite_version()
        return (version,) + get_version_tuple(version)

    def spatial_aggregate_name(self, agg_name):
        
        agg_name = "unionagg" if agg_name.lower() == "union" else agg_name.lower()
        return getattr(self, agg_name)

    
    def geometry_columns(self):
        from django.contrib.gis.db.backends.spatialite.models import (
            SpatialiteGeometryColumns,
        )

        return SpatialiteGeometryColumns

    def spatial_ref_sys(self):
        from django.contrib.gis.db.backends.spatialite.models import (
            SpatialiteSpatialRefSys,
        )

        return SpatialiteSpatialRefSys

    def get_geometry_converter(self, expression):
        geom_class = expression.output_field.geom_class
        read = wkb_r().read

        def converter(value, expression, connection):
            return None if value is None else GEOSGeometryBase(read(value), geom_class)

        return converter