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 Geoconenx 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.