# Geo Functions

SemQL has two built-in geo functions for working with coordinates: `GEO_DISTANCE()` and `GEO_WITHIN_RADIUS()`. Both take latitude/longitude pairs and return results you can use directly in your queries.

These functions are database-agnostic — they work regardless of which database you're connected to (Snowflake, BigQuery, Redshift, etc.), because SemQL compiles them to standard trigonometry (the Haversine formula) that runs on any SQL engine.

***

## GEO\_DISTANCE

Returns the distance in **kilometers** between two geographic coordinates.

### Syntax

```sql
GEO_DISTANCE(lat1, lng1, lat2, lng2)
```

| Parameter | Type   | Description                   |
| --------- | ------ | ----------------------------- |
| `lat1`    | number | Latitude of the first point   |
| `lng1`    | number | Longitude of the first point  |
| `lat2`    | number | Latitude of the second point  |
| `lng2`    | number | Longitude of the second point |

### Examples

**Distance from a fixed point to a column value:**

```sql
SELECT
    store_name,
    city,
    GEO_DISTANCE(48.8566, 2.3522, store_lat, store_lng) AS distance_from_paris_km
FROM retail_stores
ORDER BY distance_from_paris_km ASC
```

**Filter by a calculated distance:**

```sql
SELECT
    store_name,
    GEO_DISTANCE(51.5074, -0.1278, store_lat, store_lng) AS distance_km
FROM retail_stores
WHERE GEO_DISTANCE(51.5074, -0.1278, store_lat, store_lng) <= 50
ORDER BY distance_km
```

**Distance between two columns (e.g., customer location to nearest warehouse):**

```sql
SELECT
    order_id,
    customer_name,
    GEO_DISTANCE(customer_lat, customer_lng, warehouse_lat, warehouse_lng) AS delivery_distance_km
FROM orders
JOIN customers ON orders.customer_id = customers.customer_id
JOIN warehouses ON orders.warehouse_id = warehouses.warehouse_id
ORDER BY delivery_distance_km DESC
LIMIT 20
```

***

## GEO\_WITHIN\_RADIUS

Returns `true` if a point is within a given radius of another point, `false` otherwise. Useful for filtering by proximity.

### Syntax

```sql
GEO_WITHIN_RADIUS(lat1, lng1, lat2, lng2, radius_km)
```

| Parameter   | Type   | Description                      |
| ----------- | ------ | -------------------------------- |
| `lat1`      | number | Latitude of the reference point  |
| `lng1`      | number | Longitude of the reference point |
| `lat2`      | number | Latitude of the point to test    |
| `lng2`      | number | Longitude of the point to test   |
| `radius_km` | number | Radius in kilometers             |

### Examples

**Filter stores within 25 km of a city:**

```sql
SELECT store_name, city, store_lat, store_lng
FROM retail_stores
WHERE GEO_WITHIN_RADIUS(52.3676, 4.9041, store_lat, store_lng, 25)
ORDER BY store_name
```

**Categorize records by proximity:**

```sql
SELECT
    customer_name,
    city,
    CASE
        WHEN GEO_WITHIN_RADIUS(48.8566, 2.3522, customer_lat, customer_lng, 50)
            THEN 'Within 50km of Paris'
        WHEN GEO_WITHIN_RADIUS(48.8566, 2.3522, customer_lat, customer_lng, 200)
            THEN 'Within 200km of Paris'
        ELSE 'Outside 200km'
    END AS proximity_band
FROM customers
ORDER BY proximity_band
```

**Count customers within a radius:**

```sql
SELECT
    COUNT(*) AS customer_count
FROM customers
WHERE GEO_WITHIN_RADIUS(40.7128, -74.0060, customer_lat, customer_lng, 100)
```

***

## Combining both functions

A common pattern is to show the actual distance alongside a proximity flag:

```sql
SELECT
    store_name,
    city,
    GEO_DISTANCE(50.8503, 4.3517, store_lat, store_lng) AS distance_from_brussels_km,
    GEO_WITHIN_RADIUS(50.8503, 4.3517, store_lat, store_lng, 100) AS within_100km
FROM retail_stores
ORDER BY distance_from_brussels_km
```

***

## Notes

* Coordinates must be numeric (decimal degrees). Null coordinates will cause an error.
* Distances are always in **kilometers**.
* The Haversine formula assumes a spherical Earth — it's accurate to within \~0.5% for most practical distances.
* Both functions are compiled to standard SQL math functions (`SIN`, `COS`, `ACOS`, `RADIANS`) and work on all supported database dialects.
