# R SDK

# Guia do Usuario -- pacote prismadata para R

SDK oficial em R para a API de inteligencia geoespacial da PrismaData (`https://api.prismadata.io/v1`). Este guia cobre instalacao, autenticacao, consultas individuais e em lote, enriquecimento de data.frames, integracao com tidymodels e tratamento de erros.

---

## Sumario

1. [Instalacao](#1-instalacao)
2. [Quick Start](#2-quick-start)
3. [Autenticacao](#3-autenticacao)
4. [Operacoes em Lote](#4-operacoes-em-lote)
5. [Enriquecimento de Data.Frames](#5-enriquecimento-de-dataframes)
6. [Pipeline tidymodels](#6-pipeline-tidymodels)
7. [Tratamento de Erros](#7-tratamento-de-erros)
8. [Falhas Parciais em Batch](#8-falhas-parciais-em-batch)

---

## 1. Instalacao

### Via CRAN

Quando disponivel no CRAN, basta executar:

```r
install.packages("prismadata")
```

### Via GitHub (versao de desenvolvimento)

Para instalar a versao mais recente diretamente do repositorio:

```r
# Instale o pacote remotes caso ainda nao tenha
install.packages("remotes")

# Instale o prismadata a partir do GitHub
remotes::install_github("prismadata/prismadata-r")
```

### Dependencias opcionais

O pacote possui dependencias opcionais que habilitam funcionalidades extras. Instale conforme a necessidade:

```r
# Cache em disco (para evitar chamadas repetidas a API)
install.packages("cachem")

# Enriquecimento de tibbles e manipulacao com dplyr
install.packages(c("tibble", "dplyr"))

# Integracao com tidymodels (step_prismadata)
install.packages("recipes")
```

---

## 2. Quick Start

Exemplo minimo para comecar a usar o pacote: criar um cliente, consultar a proximidade de favela para um par de coordenadas e inspecionar o resultado.

```r
library(prismadata)

# Cria o cliente com sua chave de API
client <- prismadata_client(api_key = "ak_xxxxxxxxxxxxxxxx")

# Consulta a favela mais proxima de um ponto em Sao Paulo
resultado <- client$slum(lat = -23.55, lng = -46.63)

# Inspeciona o retorno
str(resultado)
# $ prismadata__favela__nome        : chr "Paraisopolis"
# $ prismadata__favela__distancia_m : num 3420
# $ prismadata__favela__area_ha     : num 102.3
# ...
```

O objeto `client` expoe metodos para todos os servicos da API: geocodificacao, inteligencia de localizacao, roteamento, validacao de enderecos e consultas de credito.

```r
# Geocodificacao
geo <- client$geocode(full_address = "Av Paulista 1000, Sao Paulo, SP")

# Geocodificacao reversa
endereco <- client$reverse_geocode(lat = -23.5632, lng = -46.6542)

# Proximidade de presidio
presidio <- client$prison(lat = -23.55, lng = -46.63)

# Proximidade de fronteira
fronteira <- client$border(lat = -23.55, lng = -46.63)

# Informacoes do setor censitario (IBGE)
setor <- client$infosc(lat = -23.55, lng = -46.63)

# Estimativa de renda estatica
renda <- client$income_static(lat = -23.55, lng = -46.63)

# Calculo de rota entre dois pontos
rota <- client$route(
  points = list(c(-23.55, -46.63), c(-22.90, -43.17)),
  profile = "car"
)

# Isocrona (area alcancavel em 10 minutos de carro)
iso <- client$isochrone(lat = -23.55, lng = -46.63, time_limit = 600, profile = "car")
```

---

## 3. Autenticacao

O pacote suporta tres formas de autenticacao, avaliadas na seguinte ordem de prioridade:

### Metodo 1 -- Chave de API (api_key)

A forma mais simples. Voce recebe a chave no painel da PrismaData.

```r
client <- prismadata_client(api_key = "ak_xxxxxxxxxxxxxxxx")
```

### Metodo 2 -- Usuario e senha (username / password)

Autentica via endpoint `/auth/token`, obtendo um JWT que e renovado automaticamente.

```r
client <- prismadata_client(
  username = "usuario@empresa.com",
  password = "minha_senha_segura"
)
```

### Metodo 3 -- Variaveis de ambiente

Quando nenhum argumento e passado, o pacote busca credenciais automaticamente nas variaveis de ambiente. Isso e ideal para ambientes de producao, CI/CD e containers.

```r
# Opcao A: defina a variavel PRISMADATA_APIKEY
Sys.setenv(PRISMADATA_APIKEY = "ak_xxxxxxxxxxxxxxxx")

# Opcao B: defina PRISMADATA_USERNAME e PRISMADATA_PASSWORD
Sys.setenv(PRISMADATA_USERNAME = "usuario@empresa.com")
Sys.setenv(PRISMADATA_PASSWORD = "minha_senha_segura")

# O cliente detecta as credenciais automaticamente
client <- prismadata_client()
```

A ordem de resolucao e:

1. Argumento `api_key` explicito
2. Argumentos `username` + `password` explicitos
3. Variavel de ambiente `PRISMADATA_APIKEY`
4. Variaveis de ambiente `PRISMADATA_USERNAME` + `PRISMADATA_PASSWORD`

Se nenhuma credencial for encontrada, o pacote emite um erro do tipo `prismadata_authentication_error` com instrucoes de configuracao.

### Opcoes adicionais do cliente

```r
client <- prismadata_client(
  api_key = "ak_xxxxxxxxxxxxxxxx",
  timeout = 60,                # Timeout de requisicao em segundos (padrao: 30)
  cache = TRUE,                # Habilita cache em disco (requer pacote cachem)
  cache_ttl = 86400,           # Tempo de vida do cache em segundos (padrao: 24h)
  clean_columns = TRUE,        # Remove prefixo "prismadata__" dos nomes de colunas
  show_progress = TRUE,        # Exibe barra de progresso em operacoes batch
  app_name = "meu_app"        # Nome da aplicacao enviado no header X-App
)
```

### Verificando a autenticacao

```r
# Retorna informacoes do usuario autenticado
info <- client$me()
print(info)
# $username
# [1] "usuario@empresa.com"
# $roles
# [1] "admin" "full_reader"
```

---

## 4. Operacoes em Lote

Para consultar muitos pontos de uma vez, use os metodos `*_batch()`. O pacote faz o auto-chunking automaticamente: listas com mais de 1024 pontos sao divididas em blocos e enviadas sequencialmente, com barra de progresso.

### slum_batch() -- Proximidade de favela em lote

```r
# Cria uma lista nomeada de coordenadas (lat, lng)
pontos <- list(
  "sp_centro"  = c(-23.5505, -46.6333),
  "rj_centro"  = c(-22.9068, -43.1729),
  "bh_centro"  = c(-19.9191, -43.9386),
  "poa_centro" = c(-30.0346, -51.2177),
  "recife"     = c(-8.0476, -34.8770)
)

# Consulta em lote
resultados <- client$slum_batch(pontos)

# Cada resultado e acessado pela chave do ponto
resultados$sp_centro$prismadata__favela__distancia_m
# [1] 2145.7

resultados$rj_centro$prismadata__favela__nome
# [1] "Morro da Providencia"
```

### aggregate_batch() -- Multiplos servicos em lote

Consulta varios servicos simultaneamente para cada ponto (favela, presidio, fronteira, setor censitario).

```r
pontos <- list(
  "loc_1" = c(-23.55, -46.63),
  "loc_2" = c(-22.90, -43.17)
)

resultados <- client$aggregate_batch(
  points = pontos,
  services = c("slum", "prison", "infosc")
)

# O resultado de cada ponto contem campos de todos os servicos solicitados
resultados$loc_1$prismadata__favela__distancia_m
resultados$loc_1$prismadata__presidio__distancia_m
resultados$loc_1$prismadata__info_sc__cod_setor
```

### route_batch() -- Rotas em lote

Calcula multiplas rotas de uma vez. O limite por chunk para roteamento e de 16 itens.

```r
# Cada item deve conter um elemento "points" com a lista de coordenadas
itens_rota <- list(
  list(points = list(c(-23.55, -46.63), c(-23.56, -46.65))),
  list(points = list(c(-22.90, -43.17), c(-22.91, -43.20))),
  list(points = list(c(-19.92, -43.94), c(-19.93, -43.96)))
)

resultados <- client$route_batch(itens_rota, profile = "car")

# Estrutura do retorno
resultados$TOTAL       # Total de rotas solicitadas
resultados$SUCESSOS    # Quantidade de rotas calculadas com sucesso
resultados$FALHAS      # Quantidade de falhas
resultados$RESULTADOS  # Lista com os dados de cada rota
```

### Geocodificacao em lote

```r
# Geocodificar enderecos em lote (max 512 por chunk)
enderecos <- list(
  "p1" = list(endereco_completo = "Av Paulista 1000, Sao Paulo, SP"),
  "p2" = list(cep = "30130-165", numero = "100", logradouro = "Rua da Bahia")
)
resultados <- client$geocode_batch(enderecos)

# Geocodificacao reversa em lote (max 1024 por chunk)
pontos <- list("p1" = c(-23.55, -46.63), "p2" = c(-22.90, -43.17))
resultados <- client$reverse_geocode_batch(pontos)

# Geocodificacao + agregacao em lote
resultados <- client$geocode_aggregate_batch(
  enderecos,
  services = c("slum", "infosc")
)
# Cada resultado contem campos do geocoder + dos servicos solicitados
resultados$p1$prismadata__geocoder__latitude
resultados$p1$prismadata__slum__favela_distancia_m
```

### Outros metodos batch disponiveis

```r
# Proximidade de presidio em lote
client$prison_batch(pontos)

# Proximidade de fronteira em lote
client$border_batch(pontos)

# Setor censitario em lote
client$infosc_batch(pontos)

# Isocronas em lote
itens_iso <- list(
  list(lat = -23.55, lng = -46.63, time_limit = 600),
  list(lat = -22.90, lng = -43.17, time_limit = 300)
)
client$isochrone_batch(itens_iso, profile = "car", direction = "outgoing")
```

### Nota sobre auto-chunking

- **Servicos de localizacao** (`slum_batch`, `prison_batch`, `border_batch`, `infosc_batch`, `aggregate_batch`): limite de 1024 pontos por chunk.
- **Servicos de roteamento** (`route_batch`, `isochrone_batch`): limite de 16 itens por chunk.

Quando a quantidade de pontos excede o limite, o pacote divide automaticamente em chunks, envia cada um separadamente e consolida os resultados. A barra de progresso e exibida quando ha mais de um chunk (desative com `show_progress = FALSE` no construtor do cliente).

---

## 5. Enriquecimento de Data.Frames

O metodo `client$enrich()` recebe um data.frame com colunas de latitude e longitude e retorna o mesmo data.frame acrescido de colunas com dados geoespaciais da PrismaData.

```r
# Data.frame de entrada com coordenadas
df <- data.frame(
  id  = c("loja_sp", "loja_rj", "loja_bh"),
  lat = c(-23.5505, -22.9068, -19.9191),
  lng = c(-46.6333, -43.1729, -43.9386)
)

# Enriquece com dados de favela, setor censitario e renda
enriquecido <- client$enrich(
  df,
  lat_col = "lat",
  lng_col = "lng",
  services = c("slum", "infosc", "income_static")
)

# O data.frame agora possui colunas adicionais
names(enriquecido)
# [1] "id"  "lat"  "lng"
# [4] "prismadata__favela__distancia_m"  "prismadata__favela__nome"
# [7] "prismadata__info_sc__cod_setor"   "prismadata__renda__mediana"
# ...

# Visualiza o resultado
head(enriquecido)
```

### Usando com dplyr e tibbles

```r
library(dplyr)

enriquecido <- client$enrich(df, services = c("slum", "prison")) |>
  as_tibble() |>
  filter(prismadata__favela__distancia_m < 5000) |>
  arrange(prismadata__favela__distancia_m)
```

### Limpando nomes de colunas

Para remover o prefixo `prismadata__` e simplificar os nomes, crie o cliente com `clean_columns = TRUE`:

```r
client <- prismadata_client(
  api_key = "ak_xxxxxxxxxxxxxxxx",
  clean_columns = TRUE
)

enriquecido <- client$enrich(df, services = c("slum"))
names(enriquecido)
# [1] "id"  "lat"  "lng"  "favela_distancia_m"  "favela_nome"  ...
```

Voce tambem pode limpar nomes manualmente em qualquer momento:

```r
# Limpa nomes de um vetor de caracteres
clean_columns(c("prismadata__favela__distancia_m", "prismadata__geocoder__latitude"))
# [1] "favela_distancia_m"  "geocoder_latitude"

# Limpa nomes de um data.frame
df_limpo <- clean_columns(enriquecido)
```

---

## 6. Pipeline tidymodels

O pacote oferece o step `step_prismadata()` para integracao direta com o framework tidymodels/recipes. Ele permite adicionar features geoespaciais ao seu pipeline de machine learning de forma declarativa.

### Exemplo completo

```r
library(recipes)
library(prismadata)

# Suponha um dataset de treino com coordenadas e uma variavel alvo
dados_treino <- data.frame(
  lat    = c(-23.55, -22.90, -19.92, -30.03, -8.05),
  lng    = c(-46.63, -43.17, -43.94, -51.22, -34.88),
  valor  = c(1500, 2300, 1800, 1200, 900),
  target = c(1, 0, 1, 0, 1)
)

dados_teste <- data.frame(
  lat    = c(-23.56, -22.91),
  lng    = c(-46.64, -43.18),
  valor  = c(1600, 2100),
  target = c(1, 0)
)

# Define a receita com o step de enriquecimento PrismaData
receita <- recipe(target ~ ., data = dados_treino) |>
  step_prismadata(
    lat, lng,
    services = c("slum", "income_static"),
    api_key = "ak_xxxxxxxxxxxxxxxx"
  )

# Prepara a receita (faz as chamadas a API para os dados de treino)
receita_preparada <- prep(receita, training = dados_treino)

# Aplica a receita nos dados de teste (faz as chamadas para os dados de teste)
dados_enriquecidos <- bake(receita_preparada, new_data = dados_teste)

# O resultado e um tibble com as colunas originais + colunas de enriquecimento
print(dados_enriquecidos)
```

### Combinando com outros steps

```r
library(recipes)
library(prismadata)

receita <- recipe(target ~ ., data = dados_treino) |>
  step_prismadata(
    lat, lng,
    services = c("slum", "prison", "infosc"),
    api_key = "ak_xxxxxxxxxxxxxxxx",
    clean_columns = TRUE
  ) |>
  step_normalize(all_numeric_predictors()) |>
  step_dummy(all_nominal_predictors())
```

### Notas sobre step_prismadata

- O step requer exatamente 2 colunas (latitude e longitude, nessa ordem).
- Durante o `prep()`, uma chamada e feita com a primeira linha dos dados de treino para descobrir os nomes das colunas de enriquecimento. Em seguida, o dataset completo e enriquecido durante o `bake()`.
- O parametro `skip = TRUE` pode ser usado para pular o step durante o `bake()` (util para debug).
- Autenticacao via `username`/`password` tambem e suportada no step.

---

## 7. Tratamento de Erros

O pacote define uma hierarquia de condicoes (errors) customizadas, todas herdando de `prismadata_error`. Isso permite capturar erros especificos com `tryCatch`.

### Hierarquia de erros

| Classe | Descricao | Status HTTP |
|--------|-----------|-------------|
| `prismadata_error` | Erro generico da API | Qualquer |
| `prismadata_authentication_error` | Falha de autenticacao (credenciais invalidas, token expirado) | 401, 403 |
| `prismadata_rate_limit_error` | Limite de requisicoes excedido | 429 |
| `prismadata_quota_error` | Cota de uso esgotada | 402 |
| `prismadata_validation_error` | Parametros de entrada invalidos | 422 |
| `prismadata_batch_error` | Falha parcial em operacao batch | -- |

### Exemplo: capturando erros especificos

```r
tryCatch(

{
    resultado <- client$slum(lat = -23.55, lng = -46.63)
    print(resultado)
  },

  # Erro de autenticacao (credencial invalida ou expirada)
  prismadata_authentication_error = function(e) {
    message("Erro de autenticacao: ", conditionMessage(e))
    message("Status HTTP: ", e$status_code)
  },

  # Limite de requisicoes por segundo excedido
  prismadata_rate_limit_error = function(e) {
    message("Rate limit excedido. Aguarde antes de tentar novamente.")
    message("Status HTTP: ", e$status_code)
  },

  # Cota de uso esgotada (creditos insuficientes)
  prismadata_quota_error = function(e) {
    message("Cota esgotada: ", conditionMessage(e))
  },

  # Parametros de entrada invalidos
  prismadata_validation_error = function(e) {
    message("Dados de entrada invalidos: ", conditionMessage(e))
  },

  # Qualquer outro erro da API PrismaData
  prismadata_error = function(e) {
    message("Erro da API: ", conditionMessage(e))
    message("Status HTTP: ", e$status_code)
    message("Corpo da resposta: ", paste(e$body, collapse = ", "))
  }
)
```

### Exemplo: retry manual apos rate limit

```r
consulta_com_retry <- function(client, lat, lng, tentativas = 3) {
  for (i in seq_len(tentativas)) {
    resultado <- tryCatch(
      client$slum(lat = lat, lng = lng),

      prismadata_rate_limit_error = function(e) {
        if (i < tentativas) {
          tempo_espera <- 2^i
          message(sprintf("Rate limit atingido. Tentativa %d/%d. Aguardando %ds...",
                          i, tentativas, tempo_espera))
          Sys.sleep(tempo_espera)
          NULL
        } else {
          stop("Rate limit excedido apos ", tentativas, " tentativas.")
        }
      }
    )

    if (!is.null(resultado)) return(resultado)
  }
}

resultado <- consulta_com_retry(client, lat = -23.55, lng = -46.63)
```

### Nota sobre retries automaticos

O pacote ja implementa retry automatico com backoff exponencial para os seguintes status HTTP: 429 (rate limit), 500, 502, 503 e 504 (erros de servidor). Sao feitas ate 3 tentativas antes de emitir o erro. O header `Retry-After` e respeitado quando presente na resposta.

---

## 8. Falhas Parciais em Batch

Operacoes em lote podem falhar parcialmente: parte dos chunks retorna com sucesso enquanto outros falham (por timeout, erro de servidor etc.). Nesses casos, o pacote emite um erro do tipo `prismadata_batch_error` que carrega tanto os resultados parciais quanto as chaves que falharam.

### Estrutura do prismadata_batch_error

O erro `prismadata_batch_error` possui dois campos extras acessiveis no handler:

- **`e$partial_results`**: lista nomeada com os resultados dos chunks que tiveram sucesso.
- **`e$failed_keys`**: vetor de caracteres com as chaves (IDs dos pontos) que falharam.

### Exemplo: recuperando resultados parciais

```r
# Lista grande de pontos
pontos <- list(
  "p001" = c(-23.55, -46.63),
  "p002" = c(-22.90, -43.17),
  "p003" = c(-19.92, -43.94),
  "p004" = c(-30.03, -51.22),
  "p005" = c(-8.05,  -34.88)
  # ... imagine centenas de pontos
)

resultados <- tryCatch(
  client$slum_batch(pontos),

  # Captura falha parcial
  prismadata_batch_error = function(e) {
    message(sprintf(
      "Falha parcial: %d resultados obtidos, %d chaves falharam",
      length(e$partial_results),
      length(e$failed_keys)
    ))

    # Exibe quais chaves falharam
    message("Chaves com falha: ", paste(e$failed_keys, collapse = ", "))

    # Retorna os resultados parciais para nao perder o que ja foi processado
    e$partial_results
  },

  # Outros erros da API
  prismadata_error = function(e) {
    message("Erro geral da API: ", conditionMessage(e))
    NULL
  }
)

# Processa os resultados parciais (se houver)
if (!is.null(resultados)) {
  message(sprintf("Processando %d resultados parciais...", length(resultados)))

  for (chave in names(resultados)) {
    distancia <- resultados[[chave]]$prismadata__favela__distancia_m
    message(sprintf("  %s: %.1f metros da favela mais proxima", chave, distancia))
  }
}
```

### Exemplo: retry das chaves que falharam

```r
consulta_batch_resiliente <- function(client, pontos, max_tentativas = 3) {
  resultados_finais <- list()
  pendentes <- pontos

  for (tentativa in seq_len(max_tentativas)) {
    if (length(pendentes) == 0) break

    resultado <- tryCatch(
      client$slum_batch(pendentes),

      prismadata_batch_error = function(e) {
        # Acumula resultados parciais
        resultados_finais <<- c(resultados_finais, e$partial_results)

        # Filtra apenas os pontos que falharam para retry
        chaves_falha <- e$failed_keys
        message(sprintf(
          "Tentativa %d: %d sucessos, %d falhas. Retentando falhas...",
          tentativa,
          length(e$partial_results),
          length(chaves_falha)
        ))
        pendentes <<- pontos[chaves_falha]
        NULL
      }
    )

    if (!is.null(resultado)) {
      # Tudo funcionou nesta tentativa
      resultados_finais <- c(resultados_finais, resultado)
      pendentes <- list()
    }
  }

  if (length(pendentes) > 0) {
    warning(sprintf(
      "%d pontos nao puderam ser processados apos %d tentativas: %s",
      length(pendentes),
      max_tentativas,
      paste(names(pendentes), collapse = ", ")
    ))
  }

  resultados_finais
}

# Uso
todos_resultados <- consulta_batch_resiliente(client, pontos)
```

### Exemplo: falha parcial em route_batch

Para operacoes de roteamento em lote, a estrutura do `partial_results` e diferente -- segue o formato com `TOTAL`, `SUCESSOS`, `FALHAS` e `RESULTADOS`:

```r
itens_rota <- list(
  list(points = list(c(-23.55, -46.63), c(-23.56, -46.65))),
  list(points = list(c(-22.90, -43.17), c(-22.91, -43.20)))
)

resultado <- tryCatch(
  client$route_batch(itens_rota, profile = "car"),

  prismadata_batch_error = function(e) {
    parcial <- e$partial_results

    message(sprintf(
      "Rotas: %d total, %d sucessos, %d falhas",
      parcial$TOTAL,
      parcial$SUCESSOS,
      parcial$FALHAS
    ))

    message("Indices com falha: ", paste(e$failed_keys, collapse = ", "))

    # Retorna os resultados parciais
    parcial
  }
)
```

---

## Referencia Rapida

| Metodo | Descricao |
|--------|-----------|
| `client$geocode()` | Geocodificacao de endereco |
| `client$reverse_geocode()` | Geocodificacao reversa |
| `client$slum()` | Proximidade de favela |
| `client$prison()` | Proximidade de presidio |
| `client$border()` | Proximidade de fronteira |
| `client$infosc()` | Setor censitario (IBGE) |
| `client$income_static()` | Estimativa de renda estatica |
| `client$income_pdf()` | Funcao densidade de probabilidade de renda |
| `client$route()` | Calculo de rota |
| `client$isochrone()` | Calculo de isocrona |
| `client$compare_address()` | Comparacao de endereco vs coordenadas |
| `client$validate_address()` | Validacao de endereco via GPS |
| `client$cluster_locations()` | Clusterizacao de localizacoes historicas |
| `client$precatory()` | Consulta de precatorios/RPV |
| `client$precatory_detail()` | Detalhamento de precatorios/RPV |
| `client$aggregate()` | Multiplos servicos em uma chamada |
| `client$slum_batch()` | Favela em lote |
| `client$prison_batch()` | Presidio em lote |
| `client$border_batch()` | Fronteira em lote |
| `client$infosc_batch()` | Setor censitario em lote |
| `client$aggregate_batch()` | Multiplos servicos em lote |
| `client$geocode_batch()` | Geocodificacao em lote |
| `client$reverse_geocode_batch()` | Geocodificacao reversa em lote |
| `client$geocode_aggregate_batch()` | Geocodificacao + agregacao em lote |
| `client$route_batch()` | Rotas em lote |
| `client$isochrone_batch()` | Isocronas em lote |
| `client$enrich()` | Enriquecimento de data.frame |
| `client$me()` | Informacoes do usuario autenticado |
| `step_prismadata()` | Step para recipes/tidymodels |
| `clean_columns()` | Limpeza de nomes de colunas |

---

Para mais informacoes, consulte a documentacao da API em [https://docs.prismadata.io](https://docs.prismadata.io) ou abra uma issue no [repositorio GitHub](https://github.com/prismadata/prismadata-r/issues).

---

- Índice geral: https://prismadata.io/llms.txt
- Documentação Redoc interativa: https://prismadata.io/redoc.html
