Checking Spatial Relationships in Django: PreparedGeometry.within() Explained


Purpose

  • The within() method determines whether a given geometry (other_geom) lies entirely inside another geometry (self). In simpler terms, it checks if all points of other_geom are strictly within the boundary of self.

Functionality

    • within() operates on PreparedGeometry objects, which are optimized for spatial relationship checks like within(), contains(), intersects(), etc. To use within(), you typically create a PreparedGeometry from a regular GEOSGeometry object:
    from django.contrib.gis.geos import GEOSGeometry, PreparedGeometry
    
    # Create a regular geometry (e.g., a polygon)
    my_geom = GEOSGeometry("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))")  # Square polygon
    
    # Prepare it for spatial relationship checks
    prepared_geom = PreparedGeometry(my_geom)
    
  1. Spatial Relationship Check

    • Once you have the prepared_geom, you can call within() on it, passing another GEOSGeometry object (other_geom) as the argument:
    # Another geometry (e.g., a point inside the square)
    other_geom = GEOSGeometry("POINT(5 5)")  # Point inside the square
    
    # Check if the point is within the square
    is_within = prepared_geom.within(other_geom)
    
    • within() performs the spatial relationship check using the GEOS library, which is a powerful C++ library for geometric operations.

Return Value

  • within() returns a boolean value:
    • True if other_geom is entirely within self.
    • False otherwise (if other_geom intersects the boundary or lies outside self).

Example

from django.contrib.gis.geos import GEOSGeometry, PreparedGeometry

# Square polygon
my_geom = GEOSGeometry("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))")
prepared_geom = PreparedGeometry(my_geom)

# Point inside the square
point_inside = GEOSGeometry("POINT(5 5)")

# Point outside the square
point_outside = GEOSGeometry("POINT(15 5)")

print(prepared_geom.within(point_inside))  # True
print(prepared_geom.within(point_outside))  # False

Additional Notes

  • Ensure that the geometries involved in the check have the same Spatial Reference System (SRS) for accurate results. You can use the srs property of GEOSGeometry objects to check their SRS.
  • within() is just one of the many spatial relationship checks available in django.contrib.gis. Other common ones include contains(), intersects(), covers(), etc.


Checking a Point Within a Polygon (More Complex Shape)

from django.contrib.gis.geos import GEOSGeometry, PreparedGeometry

# Complex polygon representing a park
park_geom = GEOSGeometry("POLYGON((0 0, 100 0, 100 50, 50 80, 0 50, 0 0))")
prepared_park_geom = PreparedGeometry(park_geom)

# Point inside the park (e.g., a picnic location)
picnic_point = GEOSGeometry("POINT(20 20)")

# Check if the picnic location is within the park
is_within_park = prepared_park_geom.within(picnic_point)
print(f"The picnic location is within the park: {is_within_park}")

Checking Multiple Points Within a Geometry

from django.contrib.gis.geos import GEOSGeometry, PreparedGeometry

# Circular region representing a delivery zone
delivery_zone_geom = GEOSGeometry("POINT(50 50) BUFFER(10)")  # Creates a circle with radius 10
prepared_delivery_zone = PreparedGeometry(delivery_zone_geom)

# List of points representing customer addresses
customer_addresses = [
    GEOSGeometry("POINT(45 55)"),  # Within the zone
    GEOSGeometry("POINT(60 40)"),  # Outside the zone
]

for address in customer_addresses:
    is_within_zone = prepared_delivery_zone.within(address)
    print(f"Customer address at {address.coords[0]} {address.coords[1]} is within the zone: {is_within_zone}")
from django.contrib.gis.geos import GEOSGeometry, Point, transform

# Point in WGS84 (geographic coordinates)
point_wgs84 = Point(10, 20, srid=4326)  # Likely latitude/longitude

# Polygon in UTM (projected coordinates)
utm_polygon = GEOSGeometry("POLYGON((120000 500000, 130000 500000, 130000 510000, 120000 510000, 120000 500000))", srid=32610)

# Transform the point to the polygon's SRS for accurate comparison
transformed_point = transform(point_wgs84, utm_polygon.srs)

# Prepare the polygon for the check
prepared_polygon = PreparedGeometry(utm_polygon)

# Check if the transformed point is within the polygon
is_within_utm_polygon = prepared_polygon.within(transformed_point)
print(f"The point (WGS84) is within the UTM polygon after transformation: {is_within_utm_polygon}")


GEOSGeometry.contains()

  • contains() is similar to within(), but it's slightly less strict. It returns True if other_geom is entirely within self, or if it intersects the boundary of self. In other words, a geometry can touch the boundary of self and still be considered "contained" according to contains().
from django.contrib.gis.geos import GEOSGeometry

# Square polygon
my_geom = GEOSGeometry("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))")

# Point on the edge of the square
point_on_edge = GEOSGeometry("POINT(10 5)")

print(my_geom.within(point_on_edge))  # False (outside)
print(my_geom.contains(point_on_edge))  # True (touches the boundary)

Custom Logic with GEOS Operations

  • This approach requires a deeper understanding of GEOS functions and may be more complex to implement.
  • For more granular control over the spatial relationship check, you can use other GEOS operations like touches(), intersects(), and disjoint() in combination with custom logic.

Third-Party Libraries (if applicable)

  • These libraries offer a wider range of spatial operations, but they might require additional setup and integration with your Django project.
  • If django.contrib.gis doesn't fully meet your needs, you could explore third-party geospatial libraries like Shapely or PostGIS functions (if using a PostgreSQL database).
  • Remember to evaluate the complexity and maintainability of your approach.
  • For more complex checks or if django.contrib.gis limitations arise, consider custom logic or third-party libraries.
  • Use contains() if boundary touchpoints are also considered "within."
  • If you need to strictly ensure a geometry lies entirely within another, within() is generally the most appropriate choice.
  • The best alternative depends on the specific spatial relationship you want to check.