Skip to main content

Spatial Queries

Bounding box​

We can also do spatial queries, using bounding box queries (in the format min lon, min lat, max lon, max lat) or by passing WKT-encoded geometry. Let's say we want to find all Public Water Systems within a bounding box around the four-corners region.

# Define the bounding box for the Four Corners area
# Format: (min Longitude, min Latitude, max Longitude, max Latitude)
bbox <- c(-109.5, 36.5, -107.5, 37.5)
# Construct the URL with the bbox parameter
url <- paste0(base_url, "/collections/pws/items?f=json&bbox=", paste(bbox, collapse = ","))
# Read the data into an sf object
four_corners_pws <- st_read(url, quiet = TRUE)
# Display summary of the results
cat("Number of Public Water Systems found:", nrow(four_corners_pws), "\n")
# Create an interactive table of the results
four_corners_pws |>
st_drop_geometry() |>
select(uri, pws_name, population_served_count) |>
arrange(desc(population_served_count)) |>
datatable(
options = list(pageLength = 5, scrollX = TRUE),
caption = "Public Water Systems in the Four Corners Area",
rownames = FALSE
)
# Create a map view of the results
m <- mapview(four_corners_pws, zcol = "population_served_count", layer.name = "Population Served", label= "pws_name")
# Add the bounding box to the map
bbox_poly <- st_as_sf(st_as_sfc(st_bbox(c(xmin = bbox[1], ymin = bbox[2], xmax = bbox[3], ymax = bbox[4]), crs = 4326)))
m + mapview(bbox_poly, col.region = "red", alpha.regions=0, color="red", lwd=2, layer.name = "Query Bounding Box")

Intersection​

When it comes to spatial queries, we are not restricted to bounding box queries. We can pass any spatial predicate along with WKT geometries to a collection filter. Let's say we have several field sites near Farmington, NM, and we want to identify which HUC10 watersheds they fall within. We'll use the point-in-polygon query capability of the INTERECTS spatial; predicate to find this information:

# Define our field site (example coordinate near Farmington, NM)
site_lon <- -108.2186
site_lat <- 36.7280
# Construct the query
query <- sprintf("INTERSECTS(geometry, POINT(%f %f))", site_lon, site_lat) |> URLencode()
url <- paste0(base_url, "/collections/hu10/items?f=json&filter=", query)
# Make the API call
huc10 <- st_read(url, quiet = TRUE) |>
select(id,uri,name)
# Display the results table
datatable(huc10)
# Create a map
site_point <- st_point(c(site_lon, site_lat)) |>
st_sfc(crs = 4326) |>
st_sf()
mapview(huc10, zcol = "name", layer.name = "HUC10 Watershed") +
mapview(site_point, col.regions = "red", layer.name = "Field Site")

Here we see that our field site is in the HUC10 1408010505, which has the associated Geoconnex URI https://geoconnex.us/ref/hu10/1408010505. This identifier can be used if we were to publish data about our site, following Geoconnex guidance and best practices.

Intersection by URL reference​

Complex features can have many coordinates, and thus requests via ?filter to the API can be too long to format as URL. To get around this, the API supports a special intersection process that involves passing a URL for any GeoJSON feature to a collection. Let's say we want to know which counties intersect the Animas River (https://geoconnex.us/ref/mainstems/35394).

# Define the process endpoint
process_url <- "https://reference.geoconnex.us/processes/intersector/execution"
# Define the input parameters
input_params <- list(
inputs = list(
url = "https://geoconnex.us/ref/mainstems/35394",
collection = "counties"
)
)
# Execute the process
response <- POST(
url = process_url,
body = toJSON(input_params, auto_unbox = TRUE),
add_headers("Content-Type" = "application/json"),
encode = "json"
)
# Convert the result to an sf object
intersecting_counties <- st_read(response, quiet = TRUE)


# Create an interactive table of the results
intersecting_counties |>
st_drop_geometry() |>
select(name, uri) |>
datatable(
options = list(pageLength = 5, scrollX = TRUE),
caption = "Counties Intersecting the Animas River"
)

# Fetch the Animas River geometry
animas_river <- st_read("https://geoconnex.us/ref/mainstems/35394", quiet = TRUE)

# Create a map view of the results
mapview(intersecting_counties, zcol = "name", layer.name = "Intersecting Counties") +
mapview(animas_river, color = "blue", layer.name = "Animas River")

Note that of the three counties intersecting the Animas River, two are named "San Juan": https://geoconnex.us/ref/counties/08111 in Colorado, and https://geoconnex.us/ref/counties/35045 in New Mexico, highlighting the importance of unique identifiers and the usefulness of HTTP identifiers that direct to spatial/data representations of a given feature.