---
title: "GISCO APIs with giscoR"
description: >
  Use the GISCO ID service API and GISCO Address API from R for geocoding,
  reverse geocoding or feature lookup from coordinates.
vignette: >
  %\VignetteIndexEntry{GISCO APIs with giscoR}
  %\VignetteEngine{quarto::html}
  %\VignetteEncoding{UTF-8}
---

<!-- apis.qmd is generated from apis.qmd.orig. Please edit that file -->



# Overview

Most **giscoR** workflows should start with the higher-level download functions,
such as `gisco_get_countries()`, `gisco_get_nuts()` or `gisco_get_lau()`.
These functions download complete GISCO datasets and return
[`sf`](https://r-spatial.github.io/sf/reference/sf.html) objects that can be
filtered, joined and mapped locally.

The GISCO APIs are useful when a workflow starts from a coordinate, address or
identifier and you need a targeted lookup instead of a complete dataset.
**giscoR** includes wrappers for two API families:

- `gisco_id_api_*()` functions query the GISCO ID service API.
- `gisco_address_api_*()` functions query the GISCO Address API.

These functions use live GISCO services. Results can change as services and
source datasets are updated. Calls to unavailable services return `NULL`.

# GISCO ID service API

The GISCO ID service API identifies GISCO features from coordinates or IDs. It
is useful when you have a point location and need to know which NUTS region,
Local Administrative Unit (LAU), country, river basin, biogeographical region
or census grid cell contains it. Coordinates must use the CRS selected by
`epsg`, which defaults to EPSG:4326. Returned geometries also use this CRS.
The package sends `epsg` as the API parameter `proj` and `nuts_level` as
`level`.

For example, the following code queries the Basque Country coordinates used in
the package examples and compares the matching NUTS 3 and LAU geometries.


``` r
library(giscoR)
library(ggplot2)

x <- -2.5
y <- 43.06

nuts3 <- gisco_id_api_nuts(x = x, y = y, nuts_level = 3)
lau <- gisco_id_api_lau(x = x, y = y)
place <- sf::st_as_sf(
  data.frame(name = "Input coordinate", x = x, y = y),
  coords = c("x", "y"),
  crs = 4326
)
```

Most ID service wrappers can return either geometry or attributes. Use
`geometry = FALSE` when you only need identifiers and metadata:


``` r
nuts <- gisco_id_api_nuts(
  x = x,
  y = y,
  nuts_level = 3,
  geometry = FALSE
)

nuts
#> # A tibble: 1 × 3
#>   nuts_id stat_levl_code OBJECTID
#>   <chr>            <int> <chr>   
#> 1 ES212                3 ES212
```

Use `geometry = TRUE`, the default, when you want an `sf` object suitable for
mapping. Successful Geonames queries return an `sf` object and use longitude
and latitude in EPSG:4326. Its bounding-box queries must span no more than
five degrees in either dimension.


``` r
ggplot(nuts3) +
  geom_sf(fill = "#cfe8f3", color = "#2a6f97", linewidth = 0.4) +
  geom_sf(data = lau, fill = "#f4a261", color = "#9c4221", linewidth = 0.3) +
  geom_sf(data = place, color = "#b00020", size = 2.5) +
  coord_sf() +
  theme_minimal() +
  theme(
    panel.background = element_rect(fill = "grey95", color = NA),
    axis.line = element_blank(),
    axis.text = element_blank(),
    panel.grid = element_blank()
  ) +
  labs(
    title = "GISCO ID service API lookup",
    subtitle = "NUTS 3 region and LAU containing the input coordinate",
    caption = gisco_attributions()
  )
```

<div class="figure">
<img src="./fig-api-id-1.png" alt="Map of the NUTS 3 region containing longitude -2.5 and latitude 43.06 in the Basque Country. A light blue region surrounds the orange municipality, and a red point marks the queried coordinate inside that municipality. The nested boundaries show that the same coordinate identifies both a region and a local administrative unit." width="100%" />
<p class="caption">NUTS 3 and LAU geometries returned by the GISCO ID service API</p>
</div>

You can also query NUTS regions by ID. The service supports identifier lookups
for other datasets and Geonames name searches, but these lookup modes are not
exposed by the corresponding **giscoR** functions:


``` r
gisco_id_api_nuts(nuts_id = "ES21", nuts_level = 2)
#> Simple feature collection with 1 feature and 1 field
#> Geometry type: MULTIPOLYGON
#> Dimension:     XY
#> Bounding box:  xmin: -3.450329 ymin: 42.47236 xmax: -1.728903 ymax: 43.45712
#> Geodetic CRS:  WGS 84
#> # A tibble: 1 × 2
#>   nuts_id                                                                              geometry
#> * <chr>                                                                      <MULTIPOLYGON [°]>
#> 1 ES21    (((-1.998404 43.32102, -1.997205 43.32199, -1.997596 43.32216, -1.998184 43.32241, -…
```

# GISCO Address API

The GISCO Address API supports address search, reverse geocoding and lookup of
available administrative address components. It can be useful when a workflow
starts with a human-readable address rather than a GISCO feature ID.

`province` and `city` are generic terms whose administrative levels vary by
country. Use the lookup helpers to inspect available address components:


``` r
gisco_address_api_countries()
#> # A tibble: 31 × 1
#>    L0   
#>    <chr>
#>  1 AT   
#>  2 BE   
#>  3 BG   
#>  4 CH   
#>  5 CY   
#>  6 CZ   
#>  7 DE   
#>  8 DK   
#>  9 EE   
#> 10 EL   
#> # ℹ 21 more rows
gisco_address_api_provinces(country = "LU")
#> # A tibble: 12 × 1
#>    L1              
#>    <chr>           
#>  1 CAPELLEN        
#>  2 CLERVAUX        
#>  3 DIEKIRCH        
#>  4 ECHTERNACH      
#>  5 ESCH-SUR-ALZETTE
#>  6 GREVENMACHER    
#>  7 LUXEMBOURG      
#>  8 MERSCH          
#>  9 REDANGE         
#> 10 REMICH          
#> 11 VIANDEN         
#> 12 WILTZ
gisco_address_api_cities(country = "LU")
#> # A tibble: 542 × 1
#>    L2        
#>    <chr>     
#>  1 ABWEILER  
#>  2 AHN       
#>  3 ALLERBORN 
#>  4 ALSCHEID  
#>  5 ALTLINSTER
#>  6 ALTRIER   
#>  7 ALTWIES   
#>  8 ALZINGEN  
#>  9 ANGELSBERG
#> 10 ANSEMBOURG
#> # ℹ 532 more rows
gisco_address_api_roads(country = "LU", province = "Capellen", city = "Dippach")
#> # A tibble: 16 × 2
#>    TF                      PC   
#>    <chr>                   <chr>
#>  1 BEIM WAASSERTUERM       4972 
#>  2 ECKERBIERG              4974 
#>  3 IM INBERG               4974 
#>  4 OP WAISSE MUOR          4974 
#>  5 ROUTE DE LUXEMBOURG     4972 
#>  6 ROUTE DE LUXEMBOURG     4973 
#>  7 ROUTE DES TROIS CANTONS 4972 
#>  8 RUE BELLE-VUE           4974 
#>  9 RUE CENTRALE            4974 
#> 10 RUE DE BETTANGE         4974 
#> 11 RUE DE HOLZEM           4974 
#> 12 RUE DE LA FONTAINE      4974 
#> 13 RUE DES ROMAINS         4974 
#> 14 RUE DU CIMETIÈRE        4974 
#> 15 RUE JEAN-PIERRE KIRSCH  4974 
#> 16 SAUER AARBECHT          4974
```

Use `gisco_address_api_search()` for structured or freeform geocoding.
Structured searches support approximate string matching, so exact spelling is
not always required. Searches return at most 1,000 addresses. The bounding-box
endpoint can provide context for the returned address points.

Freeform queries use the `q` argument to search for an address in a single
string and do not support approximate string matching. Autocomplete through
`/search?suggest=` is not implemented in **giscoR**.


``` r
freeform <- gisco_address_api_search(q = "alphonse weicker luxembourg")
```


``` r
address <- gisco_address_api_search(
  country = "LU",
  city = "Luxembourg",
  road = "Rue Alphonse Weicker"
)

bbox <- gisco_address_api_bbox(
  country = "LU",
  city = "Luxembourg"
)
```

Use `gisco_address_api_most_populated_cell(city = "Madrid")` to retrieve the
most populated census grid cell. Supply `province` or `city`, or both to
restrict the lookup to the city within the province. When a cell is found, the
function returns a one-row [tibble](https://tibble.tidyverse.org/reference/tbl_df.html)
with the API coordinates in numeric `X` and `Y` columns. The API documentation
does not specify the CRS for these coordinates, so the function preserves them
without creating a geometry.

Search and reverse geocoding return `sf` objects when coordinates are
available. Queries with no address results return an empty tibble. Bounding-box
calls return an `sf` object when a bounding box is found, or `NULL` otherwise.
If a search result contains coordinates, they can be passed to the reverse
endpoint, which returns at most five addresses:


``` r
gisco_address_api_reverse(
  x = address$X[1],
  y = address$Y[1],
  country = "LU"
)
#> Simple feature collection with 5 features and 14 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 6.16786 ymin: 49.6315 xmax: 6.169307 ymax: 49.63328
#> Geodetic CRS:  WGS 84
#> # A tibble: 5 × 15
#>   LD    TF              L2    L1    L0    I3    PC    N0    N1    N2    N3    OL        X     Y
#> * <chr> <chr>           <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <dbl> <dbl>
#> 1 4     RUE ALPHONSE W… LUXE… LUXE… LU    LUX   2721  LU    LU0   LU00  LU000 8FX8…  6.17  49.6
#> 2 3     RUE JEAN MONNET LUXE… LUXE… LU    LUX   2180  LU    LU0   LU00  LU000 8FX8…  6.17  49.6
#> 3 41B   AVENUE JOHN F.… LUXE… LUXE… LU    LUX   1855  LU    LU0   LU00  LU000 8FX8…  6.17  49.6
#> 4 2     RUE JEAN MONNET LUXE… LUXE… LU    LUX   2180  LU    LU0   LU00  LU000 8FX8…  6.17  49.6
#> 5 5     RUE ALPHONSE W… LUXE… LUXE… LU    LUX   2721  LU    LU0   LU00  LU000 8FX8…  6.17  49.6
#> # ℹ 1 more variable: geometry <POINT [°]>
```

Use the returned geometries directly with **ggplot2**:


``` r
ggplot(bbox) +
  geom_sf(fill = "#f1faee", color = "#457b9d", linewidth = 0.5) +
  geom_sf(data = address, color = "#d00000", size = 2) +
  coord_sf() +
  theme_minimal() +
  theme(
    panel.background = element_rect(fill = "grey95", color = NA),
    axis.line = element_blank(),
    axis.text = element_blank(),
    panel.grid = element_blank()
  ) +
  labs(
    title = "GISCO Address API lookup",
    subtitle = "Address search results inside the Luxembourg city bounding box",
    caption = gisco_attributions()
  )
```

<div class="figure">
<img src="./fig-address-api-1.png" alt="Map of address search results for Rue Alphonse Weicker in Luxembourg city. Red points form a tight cluster in the northeastern part of the rectangular city bounding box, outlined in blue. The box provides geographic context for the locations returned by the address search." width="100%" />
<p class="caption">Structured address search result and city bounding box</p>
</div>

# Choosing between APIs and downloads

Use download functions when you need complete datasets:

```r
countries <- gisco_get_countries(year = 2024)
nuts <- gisco_get_nuts(year = 2024, nuts_level = 2)
```

Use API functions when you need targeted answers:

- Which NUTS region contains this coordinate?
- Which LAU contains this coordinate?
- What address is closest to this coordinate?
- Which address components are available for a country, province or city?

For repeated spatial analysis over many records, downloading a full dataset and
joining locally is often more robust than making one API request per row. API
requests are best suited to interactive lookup, validation and small batches.

# Defensive use

API services are remote dependencies. In scripts, check for `NULL` before using
the result:

```r
res <- gisco_id_api_country(x = x, y = y)

if (is.null(res)) {
  message("The GISCO ID service API did not return a result.")
} else {
  res
}
```

For address searches, also check for an empty result before accessing rows.

In packages or tests, skip API-dependent checks when GISCO is not reachable.
For reproducible workflows, keep API calls close to the point where the result
is needed and avoid assuming that live services will always return exactly the
same number of rows.
