ensuring that data was loaded from initial data fixtures testing on a point making sure typeerror is thrown when trying to set with an incompatible type now setting with a compatible geos geometry saving and ensuring the save took notice no srid is explicitly set ensuring that the srid is automatically set to that of the field after assignment but before saving ensuring the point was saved correctly after saving setting the x and y of the point checking assignments pre postsave testing on a polygon creating a state object using a built polygon testing the ogr and srs lazygeometry properties changing the interior ring on the poly attribute san antonio in wgs84 srid 4326 san antonio in wgs 84 pseudomercator srid 3857 constructing querying with a point from a different srid oracle sdooverlapbdyintersect operates differently from stintersects so contains is used instead creating san antonio remember the alamo now verifying that san antonio was transformed correctly if the geometryfield srid is 1 then we shouldn t perform any transformation if the srid of the input geometry is different creating a pennsylvanian city all transformation sql will need to be performed on the parent table gis queries can be represented as strings query city objects filterpointwithinpolygon frombbox0 0 2 2 self assertin connection ops quotenamecity meta dbtable strquery query def testdumpdataloaddatacycleself out stringio originaldata listcity objects orderbyname callcommanddumpdata geoapp city stdoutout result out getvalue houston city objects getnamehouston self assertin point s houston point ewkt result reload now dumped data with namedtemporaryfilemodew suffix json as tmp tmp writeresult tmp seek0 callcommandloaddata tmp name verbosity0 self assertequaloriginaldata listcity objects orderbyname skipunlessdbfeaturesupportsemptygeometries def testemptygeometriesself geometryclasses point linestring linearring polygon multipoint multilinestring multipolygon geometrycollection for klass in geometryclasses g klasssrid4326 modelclass feature if g hasz if not connection features supports3dstorage continue else modelclass threedimensionalfeature feature modelclass objects createnamefempty klass name geomg feature refreshfromdb if klass is linearring linearring isn t representable in wkb so geosgeomtry wkb uses linestring instead g linestringsrid4326 self assertequalfeature geom g self assertequalfeature geom srid g srid class geolookuptesttestcase fixtures initial def testdisjointlookupself testing the disjoint lookup type ptown city objects getnamepueblo qs1 city objects filterpointdisjointptown point self assertequal7 qs1 count qs2 state objects filterpolydisjointptown point self assertequal1 qs2 count self assertequalkansas qs20 name def testcontainscontainedlookupsself testing the contained contains and bbcontains lookup types getting texas yes we were a country once texas country objects getnametexas seeing what cities are in texas should get houston and dallas and oklahoma city because contained only checks on the bounding box of the geometries if connection features supportscontainedlookup qs city objects filterpointcontainedtexas mpoly self assertequal3 qs count cities houston dallas oklahoma city for c in qs self assertinc name cities pulling out some cities houston city objects getnamehouston wellington city objects getnamewellington pueblo city objects getnamepueblo okcity city objects getnameoklahoma city lawrence city objects getnamelawrence now testing contains on the countries using the points for houston and wellington tx country objects getmpolycontainshouston point query wgeosgeometry nz country objects get mpolycontainswellington point hex query wewkbhex self assertequaltexas tx name self assertequalnew zealand nz name testing contains on the states using the point for lawrence ks state objects getpolycontainslawrence point self assertequalkansas ks name pueblo and oklahoma city even though ok city is within the bounding box of texas are not contained in texas or new zealand self assertequal lencountry objects filtermpolycontainspueblo point 0 query wgeosgeometry object self assertequal lencountry objects filtermpolycontainsokcity point wkt 0 query wwkt ok city is contained win bounding box of texas if connection features supportsbbcontainslookup qs country objects filtermpolybbcontainsokcity point self assertequal1 lenqs self assertequaltexas qs0 name skipunlessdbfeaturesupportscrosseslookup def testcrosseslookupself track objects createnameline1 linelinestring95 29 60 0 self assertequal track objects filter linecrosseslinestring95 0 60 29 count 1 self assertequal track objects filter linecrosseslinestring95 30 0 30 count 0 skipunlessdbfeaturesupportsisvalidlookup def testisvalidlookupself invalidgeom fromstrpolygon0 0 0 1 1 1 1 0 1 1 1 0 0 0 state objects createnameinvalid polyinvalidgeom qs state objects all if connection ops oracle kansas has adjacent vertices with distance 6 99244813842e12 which is smaller than the default oracle tolerance qs qs excludenamekansas self assertequal state objects filternamekansas polyisvalidfalse count 1 self assertequalqs filterpolyisvalidfalse count 1 self assertequalqs filterpolyisvalidtrue count qs count 1 skipunlessgislookupleft right def testleftrightlookupsself testing the left and right lookup types left a b true if xmaxa xminb right a b true if xmina xmaxb see box2dleft and box2dright in lwgeombox2dfloat4 c in postgis source getting the borders for colorado kansas coborder state objects getnamecolorado poly ksborder state objects getnamekansas poly note wellington has an x value of 174 so it will not be considered to the left of co these cities should be strictly to the right of the co border cities houston dallas oklahoma city lawrence chicago wellington qs city objects filterpointrightcoborder self assertequal6 lenqs for c in qs self assertinc name cities these cities should be strictly to the right of the ks border cities chicago wellington qs city objects filterpointrightksborder self assertequal2 lenqs for c in qs self assertinc name cities note wellington has an x value of 174 so it will not be considered to the left of co vic city objects getpointleftcoborder self assertequalvictoria vic name cities pueblo victoria qs city objects filterpointleftksborder self assertequal2 lenqs for c in qs self assertinc name cities skipunlessgislookupstrictlyabove strictlybelow def teststrictlyabovebelowlookupsself dallas city objects getnamedallas self assertquerysetequal city objects filterpointstrictlyabovedallas point orderbyname chicago lawrence oklahoma city pueblo victoria lambda b b name self assertquerysetequal city objects filterpointstrictlybelowdallas point orderbyname houston wellington lambda b b name def testequalslookupsself testing the sameas and equals lookup types pnt fromstrpoint 95 363151 29 763374 srid4326 c1 city objects getpointpnt c2 city objects getpointsameaspnt c3 city objects getpointequalspnt for c in c1 c2 c3 self assertequalhouston c name skipunlessdbfeaturesupportsnullgeometries def testnullgeometriesself testing null geometry support and the isnull lookup type creating a state with a null boundary state objects createnamepuerto rico querying for both null and nonnull values nullqs state objects filterpolyisnulltrue validqs state objects filterpolyisnullfalse puerto rico should be null it s a commonwealth unincorporated territory self assertequal1 lennullqs self assertequalpuerto rico nullqs0 name geometryfieldnone is an alias for isnulltrue self assertcountequalstate objects filterpolynone nullqs self assertcountequalstate objects excludepolynone validqs the valid states should be colorado kansas self assertequal2 lenvalidqs statenames s name for s in validqs self assertincolorado statenames self assertinkansas statenames saving another commonwealth wa null geometry nmi state objects createnamenorthern mariana islands polynone self assertisnonenmi poly assigning a geometry and saving then update back to null nmi poly polygon0 0 1 0 1 1 1 0 0 0 nmi save state objects filternamenorthern mariana islands updatepolynone self assertisnonestate objects getnamenorthern mariana islands poly skipunlessdbfeature supportsnullgeometries supportscrosseslookup supportsrelatelookup def testnullgeometriesexcludedinlookupsself valid wkt strings don t emit error logs to make things more interesting we will have our texas reference point in different srids not passing in a geometry as first param raises a typeerror when initializing the queryset making sure the right exception is raised for the given bad arguments relate works differently on oracle todo this is not quite the same as the postgis mask above testing contains relation mask testing within relation mask testing intersection relation mask with a complex geometry expression todo geoqueryset is removed organize these test better testing the extent aggregate reference query select stextentpoint from geoappcity where name houston or name dallas box96 8016128540039 29 7633724212646 95 3631439208984 32 7820587158203 testing if extent supports limit testing the makeline aggregate makeline on an inappropriate field returns simply none reference query select astextstmakelinegeoappcity point from geoappcity compare pairs of manually sorted points as the default ordering is flaky testing the union aggregate houston dallas ordering may differ depending on backend or geos version using fieldname keyword argument in one query and specifying an order in the other which should not be used because this is an aggregate method on a spatial column use distinct to force the usage of a subquery for aggregation tolerance is greater than distance between forney and dallas that s why dallas is ignored using a queryset inside a geo lookup is working using a subquery 14483 ensuring that data was loaded from initial data fixtures testing on a point making sure typeerror is thrown when trying to set with an incompatible type now setting with a compatible geos geometry saving and ensuring the save took notice no srid is explicitly set ensuring that the srid is automatically set to that of the field after assignment but before saving ensuring the point was saved correctly after saving setting the x and y of the point checking assignments pre post save testing on a polygon creating a state object using a built polygon srid auto set from none testing the ogr and srs lazy geometry properties changing the interior ring on the poly attribute san antonio in wgs84 srid 4326 our reference point in wgs84 san antonio in wgs 84 pseudo mercator srid 3857 constructing querying with a point from a different srid oracle sdo_overlapbdyintersect operates differently from st_intersects so contains is used instead creating san antonio remember the alamo now verifying that san antonio was transformed correctly if the geometryfield srid is 1 then we shouldn t perform any transformation if the srid of the input geometry is different creating a pennsylvanian city all transformation sql will need to be performed on the _parent_ table ensure point isn t deferred gis queries can be represented as strings test a dumpdata loaddata cycle with geographic data reload now dumped data linearring isn t representable in wkb so geosgeomtry wkb uses linestring instead getting texas yes we were a country once seeing what cities are in texas should get houston and dallas and oklahoma city because contained only checks on the _bounding box_ of the geometries pulling out some cities now testing contains on the countries using the points for houston and wellington query w geosgeometry query w ewkbhex testing contains on the states using the point for lawrence pueblo and oklahoma city even though ok city is within the bounding box of texas are not contained in texas or new zealand query w geosgeometry object query w wkt ok city is contained w in bounding box of texas kansas has adjacent vertices with distance 6 99244813842e 12 which is smaller than the default oracle tolerance left a b true if xmax a xmin b right a b true if xmin a xmax b see box2d_left and box2d_right in lwgeom_box2dfloat4 c in postgis source getting the borders for colorado kansas note wellington has an x value of 174 so it will not be considered to the left of co these cities should be strictly to the right of the co border these cities should be strictly to the right of the ks border note wellington has an x value of 174 so it will not be considered to the left of co creating a state with a null boundary querying for both null and non null values puerto rico should be null it s a commonwealth unincorporated territory geometryfield none is an alias for __isnull true the valid states should be colorado kansas saving another commonwealth w a null geometry assigning a geometry and saving then update back to null null features are excluded in spatial lookup functions valid wkt strings don t emit error logs to make things more interesting we will have our texas reference point in different srids not passing in a geometry as first param raises a typeerror when initializing the queryset making sure the right exception is raised for the given bad arguments relate works differently on oracle todo this is not quite the same as the postgis mask above testing contains relation mask testing within relation mask testing intersection relation mask with a complex geometry expression these lookups are tested elsewhere todo geoqueryset is removed organize these test better testing the extent aggregate reference query select st_extent point from geoapp_city where name houston or name dallas box 96 8016128540039 29 7633724212646 95 3631439208984 32 7820587158203 testing if extent supports limit testing the makeline aggregate makeline on an inappropriate field returns simply none reference query select astext st_makeline geoapp_city point from geoapp_city compare pairs of manually sorted points as the default ordering is flaky testing the union aggregate houston dallas ordering may differ depending on backend or geos version using field_name keyword argument in one query and specifying an order in the other which should not be used because this is an aggregate method on a spatial column use distinct to force the usage of a subquery for aggregation tolerance is greater than distance between forney and dallas that s why dallas is ignored using a queryset inside a geo lookup is working using a subquery 14483
from io import StringIO

from django.contrib.gis import gdal
from django.contrib.gis.db.models import Extent, MakeLine, Union, functions
from django.contrib.gis.geos import (
    GeometryCollection,
    GEOSGeometry,
    LinearRing,
    LineString,
    MultiLineString,
    MultiPoint,
    MultiPolygon,
    Point,
    Polygon,
    fromstr,
)
from django.core.files.temp import NamedTemporaryFile
from django.core.management import call_command
from django.db import DatabaseError, NotSupportedError, connection
from django.db.models import F, OuterRef, Subquery
from django.test import TestCase, skipUnlessDBFeature
from django.test.utils import CaptureQueriesContext

from ..utils import skipUnlessGISLookup
from .models import (
    City,
    Country,
    Feature,
    MinusOneSRID,
    MultiFields,
    NonConcreteModel,
    PennsylvaniaCity,
    State,
    ThreeDimensionalFeature,
    Track,
)


class GeoModelTest(TestCase):
    fixtures = ["initial"]

    def test_fixtures(self):
        "Testing geographic model initialization from fixtures."
        
        self.assertEqual(2, Country.objects.count())
        self.assertEqual(8, City.objects.count())
        self.assertEqual(2, State.objects.count())

    def test_proxy(self):
        "Testing Lazy-Geometry support (using the GeometryProxy)."
        
        pnt = Point(0, 0)
        nullcity = City(name="NullCity", point=pnt)
        nullcity.save()

        
        
        for bad in [5, 2.0, LineString((0, 0), (1, 1))]:
            with self.assertRaisesMessage(TypeError, "Cannot set"):
                nullcity.point = bad

        
        
        new = Point(5, 23)
        nullcity.point = new

        
        
        self.assertEqual(4326, nullcity.point.srid)
        nullcity.save()

        
        self.assertEqual(new, City.objects.get(name="NullCity").point)

        
        nullcity.point.x = 23
        nullcity.point.y = 5
        
        self.assertNotEqual(
            Point(23, 5, srid=4326), City.objects.get(name="NullCity").point
        )
        nullcity.save()
        self.assertEqual(
            Point(23, 5, srid=4326), City.objects.get(name="NullCity").point
        )
        nullcity.delete()

        
        shell = LinearRing((0, 0), (0, 90), (100, 90), (100, 0), (0, 0))
        inner = LinearRing((40, 40), (40, 60), (60, 60), (60, 40), (40, 40))

        
        ply = Polygon(shell, inner)
        nullstate = State(name="NullState", poly=ply)
        self.assertEqual(4326, nullstate.poly.srid)  
        nullstate.save()

        ns = State.objects.get(name="NullState")
        self.assertEqual(connection.ops.Adapter._fix_polygon(ply), ns.poly)

        
        self.assertIsInstance(ns.poly.ogr, gdal.OGRGeometry)
        self.assertEqual(ns.poly.wkb, ns.poly.ogr.wkb)
        self.assertIsInstance(ns.poly.srs, gdal.SpatialReference)
        self.assertEqual("WGS 84", ns.poly.srs.name)

        
        new_inner = LinearRing((30, 30), (30, 70), (70, 70), (70, 30), (30, 30))
        ns.poly[1] = new_inner
        ply[1] = new_inner
        self.assertEqual(4326, ns.poly.srid)
        ns.save()
        self.assertEqual(
            connection.ops.Adapter._fix_polygon(ply),
            State.objects.get(name="NullState").poly,
        )
        ns.delete()

    @skipUnlessDBFeature("supports_transform")
    def test_lookup_insert_transform(self):
        "Testing automatic transform for lookups and inserts."
        
        sa_4326 = "POINT (-98.493183 29.424170)"
        wgs_pnt = fromstr(sa_4326, srid=4326)  
        
        other_srid_pnt = wgs_pnt.transform(3857, clone=True)
        
        
        
        if connection.ops.oracle:
            tx = Country.objects.get(mpoly__contains=other_srid_pnt)
        else:
            tx = Country.objects.get(mpoly__intersects=other_srid_pnt)
        self.assertEqual("Texas", tx.name)

        
        sa = City.objects.create(name="San Antonio", point=other_srid_pnt)

        
        sa = City.objects.get(name="San Antonio")
        self.assertAlmostEqual(wgs_pnt.x, sa.point.x, 6)
        self.assertAlmostEqual(wgs_pnt.y, sa.point.y, 6)

        
        
        m1 = MinusOneSRID(geom=Point(17, 23, srid=4326))
        m1.save()
        self.assertEqual(-1, m1.geom.srid)

    def test_createnull(self):
        "Testing creating a model instance and the geometry being None"
        c = City()
        self.assertIsNone(c.point)

    def test_geometryfield(self):
        "Testing the general GeometryField."
        Feature(name="Point", geom=Point(1, 1)).save()
        Feature(name="LineString", geom=LineString((0, 0), (1, 1), (5, 5))).save()
        Feature(
            name="Polygon",
            geom=Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))),
        ).save()
        Feature(
            name="GeometryCollection",
            geom=GeometryCollection(
                Point(2, 2),
                LineString((0, 0), (2, 2)),
                Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))),
            ),
        ).save()

        f_1 = Feature.objects.get(name="Point")
        self.assertIsInstance(f_1.geom, Point)
        self.assertEqual((1.0, 1.0), f_1.geom.tuple)
        f_2 = Feature.objects.get(name="LineString")
        self.assertIsInstance(f_2.geom, LineString)
        self.assertEqual(((0.0, 0.0), (1.0, 1.0), (5.0, 5.0)), f_2.geom.tuple)

        f_3 = Feature.objects.get(name="Polygon")
        self.assertIsInstance(f_3.geom, Polygon)
        f_4 = Feature.objects.get(name="GeometryCollection")
        self.assertIsInstance(f_4.geom, GeometryCollection)
        self.assertEqual(f_3.geom, f_4.geom[2])

    @skipUnlessDBFeature("supports_transform")
    def test_inherited_geofields(self):
        "Database functions on inherited Geometry fields."
        
        PennsylvaniaCity.objects.create(
            name="Mansfield", county="Tioga", point="POINT(-77.071445 41.823881)"
        )

        
        
        qs = PennsylvaniaCity.objects.annotate(
            new_point=functions.Transform("point", srid=32128)
        )

        self.assertEqual(1, qs.count())
        for pc in qs:
            self.assertEqual(32128, pc.new_point.srid)

    def test_raw_sql_query(self):
        "Testing raw SQL query."
        cities1 = City.objects.all()
        point_select = connection.ops.select % "point"
        cities2 = list(
            City.objects.raw(
                "select id, name, %s as point from geoapp_city" % point_select
            )
        )
        self.assertEqual(len(cities1), len(cities2))
        with self.assertNumQueries(0):  
            self.assertIsInstance(cities2[0].point, Point)

    def test_gis_query_as_string(self):
        
        query = City.objects.filter(point__within=Polygon.from_bbox((0, 0, 2, 2)))
        self.assertIn(
            connection.ops.quote_name(City._meta.db_table),
            str(query.query),
        )

    def test_dumpdata_loaddata_cycle(self):
        
        out = StringIO()
        original_data = list(City.objects.order_by("name"))
        call_command("dumpdata", "geoapp.City", stdout=out)
        result = out.getvalue()
        houston = City.objects.get(name="Houston")
        self.assertIn('"point": "%s"' % houston.point.ewkt, result)

        
        with NamedTemporaryFile(mode="w", suffix=".json") as tmp:
            tmp.write(result)
            tmp.seek(0)
            call_command("loaddata", tmp.name, verbosity=0)
        self.assertEqual(original_data, list(City.objects.order_by("name")))

    @skipUnlessDBFeature("supports_empty_geometries")
    def test_empty_geometries(self):
        geometry_classes = [
            Point,
            LineString,
            LinearRing,
            Polygon,
            MultiPoint,
            MultiLineString,
            MultiPolygon,
            GeometryCollection,
        ]
        for klass in geometry_classes:
            g = klass(srid=4326)
            model_class = Feature
            if g.hasz:
                if not connection.features.supports_3d_storage:
                    continue
                else:
                    model_class = ThreeDimensionalFeature
            feature = model_class.objects.create(name=f"Empty {klass.__name__}", geom=g)
            feature.refresh_from_db()
            if klass is LinearRing:
                
                
                g = LineString(srid=4326)
            self.assertEqual(feature.geom, g)
            self.assertEqual(feature.geom.srid, g.srid)


class GeoLookupTest(TestCase):
    fixtures = ["initial"]

    def test_disjoint_lookup(self):
        "Testing the `disjoint` lookup type."
        ptown = City.objects.get(name="Pueblo")
        qs1 = City.objects.filter(point__disjoint=ptown.point)
        self.assertEqual(7, qs1.count())
        qs2 = State.objects.filter(poly__disjoint=ptown.point)
        self.assertEqual(1, qs2.count())
        self.assertEqual("Kansas", qs2[0].name)

    def test_contains_contained_lookups(self):
        "Testing the 'contained', 'contains', and 'bbcontains' lookup types."
        
        texas = Country.objects.get(name="Texas")

        
        
        
        if connection.features.supports_contained_lookup:
            qs = City.objects.filter(point__contained=texas.mpoly)
            self.assertEqual(3, qs.count())
            cities = ["Houston", "Dallas", "Oklahoma City"]
            for c in qs:
                self.assertIn(c.name, cities)

        
        houston = City.objects.get(name="Houston")
        wellington = City.objects.get(name="Wellington")
        pueblo = City.objects.get(name="Pueblo")
        okcity = City.objects.get(name="Oklahoma City")
        lawrence = City.objects.get(name="Lawrence")

        
        
        tx = Country.objects.get(mpoly__contains=houston.point)  
        nz = Country.objects.get(
            mpoly__contains=wellington.point.hex
        )  
        self.assertEqual("Texas", tx.name)
        self.assertEqual("New Zealand", nz.name)

        
        ks = State.objects.get(poly__contains=lawrence.point)
        self.assertEqual("Kansas", ks.name)

        
        
        self.assertEqual(
            len(Country.objects.filter(mpoly__contains=pueblo.point)), 0
        )  
        self.assertEqual(
            len(Country.objects.filter(mpoly__contains=okcity.point.wkt)), 0
        )  

        
        if connection.features.supports_bbcontains_lookup:
            qs = Country.objects.filter(mpoly__bbcontains=okcity.point)
            self.assertEqual(1, len(qs))
            self.assertEqual("Texas", qs[0].name)

    @skipUnlessDBFeature("supports_crosses_lookup")
    def test_crosses_lookup(self):
        Track.objects.create(name="Line1", line=LineString([(-95, 29), (-60, 0)]))
        self.assertEqual(
            Track.objects.filter(
                line__crosses=LineString([(-95, 0), (-60, 29)])
            ).count(),
            1,
        )
        self.assertEqual(
            Track.objects.filter(
                line__crosses=LineString([(-95, 30), (0, 30)])
            ).count(),
            0,
        )

    @skipUnlessDBFeature("supports_isvalid_lookup")
    def test_isvalid_lookup(self):
        invalid_geom = fromstr("POLYGON((0 0, 0 1, 1 1, 1 0, 1 1, 1 0, 0 0))")
        State.objects.create(name="invalid", poly=invalid_geom)
        qs = State.objects.all()
        if connection.ops.oracle:
            
            
            qs = qs.exclude(name="Kansas")
            self.assertEqual(
                State.objects.filter(name="Kansas", poly__isvalid=False).count(), 1
            )
        self.assertEqual(qs.filter(poly__isvalid=False).count(), 1)
        self.assertEqual(qs.filter(poly__isvalid=True).count(), qs.count() - 1)

    @skipUnlessGISLookup("left", "right")
    def test_left_right_lookups(self):
        "Testing the 'left' and 'right' lookup types."
        
        
        

        
        co_border = State.objects.get(name="Colorado").poly
        ks_border = State.objects.get(name="Kansas").poly

        
        

        
        cities = [
            "Houston",
            "Dallas",
            "Oklahoma City",
            "Lawrence",
            "Chicago",
            "Wellington",
        ]
        qs = City.objects.filter(point__right=co_border)
        self.assertEqual(6, len(qs))
        for c in qs:
            self.assertIn(c.name, cities)

        
        cities = ["Chicago", "Wellington"]
        qs = City.objects.filter(point__right=ks_border)
        self.assertEqual(2, len(qs))
        for c in qs:
            self.assertIn(c.name, cities)

        
        
        vic = City.objects.get(point__left=co_border)
        self.assertEqual("Victoria", vic.name)

        cities = ["Pueblo", "Victoria"]
        qs = City.objects.filter(point__left=ks_border)
        self.assertEqual(2, len(qs))
        for c in qs:
            self.assertIn(c.name, cities)

    @skipUnlessGISLookup("strictly_above", "strictly_below")
    def test_strictly_above_below_lookups(self):
        dallas = City.objects.get(name="Dallas")
        self.assertQuerySetEqual(
            City.objects.filter(point__strictly_above=dallas.point).order_by("name"),
            ["Chicago", "Lawrence", "Oklahoma City", "Pueblo", "Victoria"],
            lambda b: b.name,
        )
        self.assertQuerySetEqual(
            City.objects.filter(point__strictly_below=dallas.point).order_by("name"),
            ["Houston", "Wellington"],
            lambda b: b.name,
        )

    def test_equals_lookups(self):
        "Testing the 'same_as' and 'equals' lookup types."
        pnt = fromstr("POINT (-95.363151 29.763374)", srid=4326)
        c1 = City.objects.get(point=pnt)
        c2 = City.objects.get(point__same_as=pnt)
        c3 = City.objects.get(point__equals=pnt)
        for c in [c1, c2, c3]:
            self.assertEqual("Houston", c.name)

    @skipUnlessDBFeature("supports_null_geometries")
    def test_null_geometries(self):
        "Testing NULL geometry support, and the `isnull` lookup type."
        
        State.objects.create(name="Puerto Rico")

        
        nullqs = State.objects.filter(poly__isnull=True)
        validqs = State.objects.filter(poly__isnull=False)

        
        self.assertEqual(1, len(nullqs))
        self.assertEqual("Puerto Rico", nullqs[0].name)
        
        self.assertCountEqual(State.objects.filter(poly=None), nullqs)
        self.assertCountEqual(State.objects.exclude(poly=None), validqs)

        
        self.assertEqual(2, len(validqs))
        state_names = [s.name for s in validqs]
        self.assertIn("Colorado", state_names)
        self.assertIn("Kansas", state_names)

        
        nmi = State.objects.create(name="Northern Mariana Islands", poly=None)
        self.assertIsNone(nmi.poly)

        
        nmi.poly = "POLYGON((0 0,1 0,1 1,1 0,0 0))"
        nmi.save()
        State.objects.filter(name="Northern Mariana Islands").update(poly=None)
        self.assertIsNone(State.objects.get(name="Northern Mariana Islands").poly)

    @skipUnlessDBFeature(
        "supports_null_geometries", "supports_crosses_lookup", "supports_relate_lookup"
    )
    def test_null_geometries_excluded_in_lookups(self):
        
        null = State.objects.create(name="NULL", poly=None)
        queries = [
            ("equals", Point(1, 1)),
            ("disjoint", Point(1, 1)),
            ("touches", Point(1, 1)),
            ("crosses", LineString((0, 0), (1, 1), (5, 5))),
            ("within", Point(1, 1)),
            ("overlaps", LineString((0, 0), (1, 1), (5, 5))),
            ("contains", LineString((0, 0), (1, 1), (5, 5))),
            ("intersects", LineString((0, 0), (1, 1), (5, 5))),
            ("relate", (Point(1, 1), "T*T***FF*")),
            ("same_as", Point(1, 1)),
            ("exact", Point(1, 1)),
            ("coveredby", Point(1, 1)),
            ("covers", Point(1, 1)),
        ]
        for lookup, geom in queries:
            with self.subTest(lookup=lookup):
                self.assertNotIn(
                    null, State.objects.filter(**{"poly__%s" % lookup: geom})
                )

    def test_wkt_string_in_lookup(self):
        
        with self.assertNoLogs("django.contrib.gis", "ERROR"):
            State.objects.filter(poly__intersects="LINESTRING(0 0, 1 1, 5 5)")

    @skipUnlessDBFeature("supports_relate_lookup")
    def test_relate_lookup(self):
        "Testing the 'relate' lookup type."
        
        
        pnt1 = fromstr("POINT (649287.0363174 4177429.4494686)", srid=2847)
        pnt2 = fromstr("POINT(-98.4919715741052 29.4333344025053)", srid=4326)

        
        
        with self.assertRaises(ValueError):
            Country.objects.filter(mpoly__relate=(23, "foo"))

        
        
        for bad_args, e in [
            ((pnt1, 0), ValueError),
            ((pnt2, "T*T***FF*", 0), ValueError),
        ]:
            qs = Country.objects.filter(mpoly__relate=bad_args)
            with self.assertRaises(e):
                qs.count()

        contains_mask = "T*T***FF*"
        within_mask = "T*F**F***"
        intersects_mask = "T********"
        
        if connection.ops.oracle:
            contains_mask = "contains"
            within_mask = "inside"
            
            intersects_mask = "overlapbdyintersect"

        
        if connection.features.supports_transform:
            self.assertEqual(
                Country.objects.get(mpoly__relate=(pnt1, contains_mask)).name,
                "Texas",
            )
        self.assertEqual(
            "Texas", Country.objects.get(mpoly__relate=(pnt2, contains_mask)).name
        )

        
        ks = State.objects.get(name="Kansas")
        self.assertEqual(
            "Lawrence", City.objects.get(point__relate=(ks.poly, within_mask)).name
        )

        
        if not connection.ops.oracle:
            if connection.features.supports_transform:
                self.assertEqual(
                    Country.objects.get(mpoly__relate=(pnt1, intersects_mask)).name,
                    "Texas",
                )
            self.assertEqual(
                "Texas", Country.objects.get(mpoly__relate=(pnt2, intersects_mask)).name
            )
            self.assertEqual(
                "Lawrence",
                City.objects.get(point__relate=(ks.poly, intersects_mask)).name,
            )

        
        mask = "anyinteract" if connection.ops.oracle else within_mask
        self.assertFalse(
            City.objects.exclude(
                point__relate=(functions.Union("point", "point"), mask)
            )
        )

    def test_gis_lookups_with_complex_expressions(self):
        multiple_arg_lookups = {
            "dwithin",
            "relate",
        }  
        lookups = connection.ops.gis_operators.keys() - multiple_arg_lookups
        self.assertTrue(lookups, "No lookups found")
        for lookup in lookups:
            with self.subTest(lookup):
                City.objects.filter(
                    **{"point__" + lookup: functions.Union("point", "point")}
                ).exists()

    def test_subquery_annotation(self):
        multifields = MultiFields.objects.create(
            city=City.objects.create(point=Point(1, 1)),
            point=Point(2, 2),
            poly=Polygon.from_bbox((0, 0, 2, 2)),
        )
        qs = MultiFields.objects.annotate(
            city_point=Subquery(
                City.objects.filter(
                    id=OuterRef("city"),
                ).values("point")
            ),
        ).filter(
            city_point__within=F("poly"),
        )
        self.assertEqual(qs.get(), multifields)


class GeoQuerySetTest(TestCase):
    
    fixtures = ["initial"]

    @skipUnlessDBFeature("supports_extent_aggr")
    def test_extent(self):
        
        
        
        
        
        
        expected = (
            -96.8016128540039,
            29.7633724212646,
            -95.3631439208984,
            32.782058715820,
        )

        qs = City.objects.filter(name__in=("Houston", "Dallas"))
        extent = qs.aggregate(Extent("point"))["point__extent"]
        for val, exp in zip(extent, expected):
            self.assertAlmostEqual(exp, val, 4)
        self.assertIsNone(
            City.objects.filter(name=("Smalltown")).aggregate(Extent("point"))[
                "point__extent"
            ]
        )

    @skipUnlessDBFeature("supports_extent_aggr")
    def test_extent_with_limit(self):
        
        extent1 = City.objects.aggregate(Extent("point"))["point__extent"]
        extent2 = City.objects.all()[:3].aggregate(Extent("point"))["point__extent"]
        self.assertNotEqual(extent1, extent2)

    def test_make_line(self):
        
        if not connection.features.supports_make_line_aggr:
            with self.assertRaises(NotSupportedError):
                City.objects.aggregate(MakeLine("point"))
            return

        
        self.assertIsNone(State.objects.aggregate(MakeLine("poly"))["poly__makeline"])
        
        
        line = City.objects.aggregate(MakeLine("point"))["point__makeline"]
        ref_points = City.objects.values_list("point", flat=True)
        self.assertIsInstance(line, LineString)
        self.assertEqual(len(line), ref_points.count())
        
        
        for point, ref_city in zip(sorted(line), sorted(ref_points)):
            point_x, point_y = point
            self.assertAlmostEqual(point_x, ref_city.x, 5)
            self.assertAlmostEqual(point_y, ref_city.y, 5)

    @skipUnlessDBFeature("supports_union_aggr")
    def test_unionagg(self):
        
        tx = Country.objects.get(name="Texas").mpoly
        
        union = GEOSGeometry("MULTIPOINT(-96.801611 32.782057,-95.363151 29.763374)")
        qs = City.objects.filter(point__within=tx)
        with self.assertRaises(ValueError):
            qs.aggregate(Union("name"))
        
        
        
        u1 = qs.aggregate(Union("point"))["point__union"]
        u2 = qs.order_by("name").aggregate(Union("point"))["point__union"]
        self.assertTrue(union.equals(u1))
        self.assertTrue(union.equals(u2))
        qs = City.objects.filter(name="NotACity")
        self.assertIsNone(qs.aggregate(Union("point"))["point__union"])

    @skipUnlessDBFeature("supports_union_aggr")
    def test_geoagg_subquery(self):
        tx = Country.objects.get(name="Texas")
        union = GEOSGeometry("MULTIPOINT(-96.801611 32.782057,-95.363151 29.763374)")
        
        with CaptureQueriesContext(connection) as ctx:
            self.assertIs(
                union.equals(
                    City.objects.filter(point__within=tx.mpoly)
                    .distinct()
                    .aggregate(
                        Union("point"),
                    )["point__union"],
                ),
                True,
            )
        self.assertIn("subquery", ctx.captured_queries[0]["sql"])

    @skipUnlessDBFeature("supports_tolerance_parameter")
    def test_unionagg_tolerance(self):
        City.objects.create(
            point=fromstr("POINT(-96.467222 32.751389)", srid=4326),
            name="Forney",
        )
        tx = Country.objects.get(name="Texas").mpoly
        
        
        forney_houston = GEOSGeometry(
            "MULTIPOINT(-95.363151 29.763374, -96.467222 32.751389)",
            srid=4326,
        )
        self.assertIs(
            forney_houston.equals_exact(
                City.objects.filter(point__within=tx).aggregate(
                    Union("point", tolerance=32000),
                )["point__union"],
                tolerance=10e-6,
            ),
            True,
        )

    @skipUnlessDBFeature("supports_tolerance_parameter")
    def test_unionagg_tolerance_escaping(self):
        tx = Country.objects.get(name="Texas").mpoly
        with self.assertRaises(DatabaseError):
            City.objects.filter(point__within=tx).aggregate(
                Union("point", tolerance="0.05))), (((1"),
            )

    def test_within_subquery(self):
        
        tex_cities = City.objects.filter(
            point__within=Country.objects.filter(name="Texas").values("mpoly")
        ).order_by("name")
        self.assertEqual(
            list(tex_cities.values_list("name", flat=True)), ["Dallas", "Houston"]
        )

    def test_non_concrete_field(self):
        NonConcreteModel.objects.create(point=Point(0, 0), name="name")
        list(NonConcreteModel.objects.all())

    def test_values_srid(self):
        for c, v in zip(City.objects.all(), City.objects.values()):
            self.assertEqual(c.point.srid, v["point"].srid)