# spatial.spatial_query { #soildb.spatial.spatial_query }

```python
spatial.spatial_query(
    geometry,
    table='mupolygon',
    return_type='tabular',
    spatial_relation='intersects',
    what=None,
    geom_column=None,
    client=None,
)
```

Execute a spatial query against SSURGO data (CANONICAL FUNCTION).

This is the primary and recommended spatial query function for py-soildb.
It is similar to soilDB::SDA_spatialQuery() in R and supports arbitrary input
geometries with flexible table and return type options.

**WHEN TO USE THIS:**
- You need to query spatial features (map units, survey areas, features)
- You have point, polygon, line, or bounding box geometry
- You want tabular or spatial results
- You prefer a single unified function for all spatial queries

**DESIGN:**
- Canonical function: ALL spatial queries should use this function
- Supports multiple geometry input formats (WKT, shapely, bbox dict)
- Flexible table selection (mupolygon, sapolygon, featpoint, featline)
- Flexible return types (tabular for analysis, spatial with geometry)
- Configurable spatial relationships (intersects, contains, within, etc.)

**PERFORMANCE NOTES:**
- Tabular queries use optimized UDFs when possible (faster than spatial joins)
- Spatial queries with geometry return large result sets; use selective bounding boxes
- Complex geometries (many vertices) may timeout; simplify when possible
- Point queries are fastest; polygon queries scale with geometry complexity
- Bounding box queries are fast and recommended for large areas

**TABLE TYPES:**
- "mupolygon": Map unit polygons (most common)
- "sapolygon": Survey area polygons
- "mupoint": Map unit points
- "muline": Map unit lines
- "featpoint": Feature points (e.g., soil pits, monuments)
- "featline": Feature lines (e.g., linear features)
- "mapunit": Map unit table (spatial join required)
- "legend": Legend table (for legend-based queries)

**SPATIAL RELATIONSHIPS:**
- "intersects": Overlaps or touches (default, most common)
- "contains": Query geometry contains SSURGO feature
- "within": Query geometry within SSURGO feature
- "touches": Features share a boundary but don't overlap
- "crosses": Features cross each other
- "overlaps": Features overlap but one doesn't contain the other

**RETURN TYPES:**
- "tabular": Returns attribute data as DataFrame (lighter weight, faster)
- "spatial": Returns with geometry column (heavier, for mapping/analysis)

**GEOMETRY INPUT FORMATS:**
- WKT string: "POINT(-94.68 42.03)" or "POLYGON((-94.7 42.0, ...))"
- Shapely geometry: geometry.Point(-94.68, 42.03)
- Bounding box dict: {"xmin": -94.7, "ymin": 42.0, "xmax": -94.6, "ymax": 42.1}

Args:
    geometry: Input geometry as WKT string, shapely geometry object, or bbox dict
    table: Target SSURGO table (default: "mupolygon"). See TABLE TYPES above.
    return_type: "tabular" for data only (default), "spatial" for with geometry
    spatial_relation: Type of spatial relationship to test (default: "intersects")
    what: Custom comma-separated column list (e.g., "mukey,muname,musym").
          If None, uses sensible defaults based on table and return_type.
    geom_column: Custom geometry column name (e.g., "geometry", "the_geom").
                 If None, auto-detected based on table.
    client: Optional SDA client instance. If not provided, a temporary client is created and closed automatically.

Returns:
    SDAResponse: Query results with methods like .to_pandas(), .to_geodataframe()

Raises:
    ValueError: If geometry cannot be parsed

Examples:
    # Example 1: Get map unit info for a point (simplest case)
    ```python
    point_wkt = "POINT(-94.6859 42.0285)"
    response = await spatial_query(point_wkt, "mupolygon", "tabular")
    df = response.to_pandas()
    print(df[['mukey', 'muname', 'musym']])
    ```

    # Example 2: Get spatial polygons in a bounding box (for mapping)
    ```python
    bbox = {"xmin": -94.7, "ymin": 42.0, "xmax": -94.6, "ymax": 42.1}
    response = await spatial_query(bbox, "mupolygon", "spatial")
    gdf = response.to_geodataframe()
    gdf.plot()  # Visualize map units
    ```

    # Example 3: Get survey areas intersecting a custom polygon
    ```python
    polygon_wkt = "POLYGON((-94.7 42.0, -94.6 42.0, -94.6 42.1, -94.7 42.1, -94.7 42.0))"
    response = await spatial_query(polygon_wkt, "sapolygon", "tabular")
    df = response.to_pandas()
    print(df[['areasymbol', 'areaname', 'areaacres']])
    ```

    # Example 4: Use shapely geometry
    ```python
    from shapely.geometry import Point
    location = Point(-94.68, 42.03)
    response = await spatial_query(location, "mupolygon", "tabular")
    ```

    # Example 5: Get feature points within area (archeological/historical sites)
    ```python
    response = await spatial_query(point_wkt, "featpoint", "spatial")
    gdf = response.to_geodataframe()
    ```

    # Example 6: Custom columns and spatial relationship
    ```python
    response = await spatial_query(
        bbox,
        table="mupolygon",
        return_type="tabular",
        spatial_relation="within",
        what="mukey,muname,musym,compname,comppct_r"
    )
    ```

**COMPARISON WITH CONVENIENCE FUNCTIONS:**

For common use cases, consider using these instead:
- `point_query(lat, lon, table, return_type)` - Simplified point queries
- `bbox_query(xmin, ymin, xmax, ymax, table, return_type)` - Simplified bbox queries
- `mupolygon_in_bbox(xmin, ymin, xmax, ymax, ...)` - Quick map unit lookups
- `sapolygon_in_bbox(xmin, ymin, xmax, ymax, ...)` - Quick survey area lookups

**DEPRECATION NOTE:**
- The functions query_mupolygon(), query_sapolygon(), query_featpoint(),
  query_featline() are deprecated. Use spatial_query() instead.
- They now wrap this function for backward compatibility.
- SpatialQueryBuilder is an internal implementation detail; use spatial_query() instead.

**SEE ALSO:**
- point_query() - Simplified point-based queries
- bbox_query() - Simplified bounding box queries
- Query builder (query.py) - For non-spatial attribute queries
- soilDB R package documentation - For additional spatial query patterns