Documentación Eviav
API REST para mapas, geocoding, ruteo, IA geoespacial. Sobre datos abiertos, pago por uso con free tier generoso por cada servicio.
Quickstart
5 pasos para hacer tu primera request.
- Crea tu cuenta. Ve a /register y registra una cuenta business.
- Genera una API key. En /dashboard/api-keys elige los servicios que vas a consumir y crea la key. Copia el texto plano — solo se muestra una vez.
- Carga una tarjeta (opcional hasta el free tier) en /dashboard/facturacion. Hacemos un cargo de USD 1 que se reembolsa automáticamente para verificar.
- Haz tu primera request:
curl "https://api.eviav.com/v1/geocode?q=Av.%20Amazonas%20Quito" \ -H "x-api-key: TU_KEY" - Monitorea uso y costo en /dashboard/uso y /dashboard/facturacion.
Autenticación
Toda request a api.eviav.com requiere una API key válida. Hay dos formas equivalentes de enviarla:
# Header recomendado:
curl https://api.eviav.com/v1/geocode?q=quito -H "x-api-key: TU_KEY"
# Alternativa estándar Bearer:
curl https://api.eviav.com/v1/geocode?q=quito -H "Authorization: Bearer TU_KEY"Las keys tienen dos entornos: live (producción, factura uso real) y test (no factura, free tier de 10k req/mes combinados). El prefijo de la key indica el entorno: eviav_live_… / eviav_test_….
Cada key puede limitarse a un subconjunto de servicios (scopes). Si una key no tiene scopes, accede a todos. Una key sin scopes y otra con scopes específicos al mismo servicio tienen el mismo permiso de acceso para ese servicio.
CORS y uso en el navegador
La API refleja el header Origin (Access-Control-Allow-Origin = el Origin de la petición) para que MapLibre y fetch funcionen cross-origin desde cualquier sitio. Esto NO es un vector: la autorización es por API key en el header/query, no por cookie/sesión — un sitio ajeno no posee tu key. Los métodos permitidos están acotados (GET, POST, PUT, DELETE, OPTIONS) y el preflight se cachea. Para keys de browser, restringe los dominios permitidos en el dashboard: esa validación se aplica al verificar la key en el request real (el preflight OPTIONS es anónimo y no puede gatearse por key). Mantén las keys con scopes mínimos (p. ej. solo tiles) en el cliente.
Código de implementación
La API es REST estándar con JSON — funciona desde cualquier lenguaje con un cliente HTTP, sin necesidad de instalar nada. Aquí tienes wrappers listos para copiar-pegar en los stacks más comunes.
Importante: nunca expongas tu API key live en código que corre en el browser. Para apps web, ponla en variables de entorno del backend y proxea las requests, o usa una key con scopes limitados (ej. solo tiles) con restricciones de dominio configuradas en el dashboard.
OpenAPI, SDKs y Postman
Tres formas de integrar Eviav sin escribir un wrapper HTTP a mano:
- Spec OpenAPI 3.1 — fuente única de verdad descargable.
https://api.eviav.com/openapi.json. Úsalo con cualquier generator (openapi-typescript, autorest, openapi-generator, etc.) para crear tu propio cliente tipado. Para probar endpoints en vivo, usá elPlayground. - SDK TypeScript oficial — instala
npm install @eviav/api-client(Node 20+, Bun, Deno, browser). Ya tipado, con retries automáticos. - SDK wrapper de MapLibre — instala
npm install @eviav/mapspara mapas interactivos en browser con tiles autenticados Eviav. - SDK Flutter/Dart oficial — en pub.dev:
flutter pub add eviav(cliente tipado para apps Flutter y Dart backend, con retries automáticos). - Colección Postman / Insomnia / Bruno — descarga eviav.postman_collection.json (18 endpoints con sample requests). Importa en Postman, configura tu API key en variables y prueba inmediatamente.
Node / TypeScript
SDK oficial (recomendado) — npm install @eviav/api-client. Tipado, con retries automáticos. Node 20+, Bun, Deno y browser.
import { EviavClient } from "@eviav/api-client";
const client = new EviavClient({ apiKey: process.env.EVIAV_API_KEY! });
const { results } = await client.geocode({ q: "Av. Amazonas, Quito" });
console.log(results[0].lat, results[0].lon);
const r = await client.directions([[-78.51, -0.22], [-78.48, -0.12]], { steps: true });
console.log(r.routes[0].distance, "m,", r.routes[0].duration, "s");O sin dependencias, un wrapper minimalista con fetch nativo (Node 20+):
// lib/eviav.ts
const BASE = process.env.EVIAV_BASE_URL ?? "https://api.eviav.com";
const KEY = process.env.EVIAV_API_KEY!;
async function eviav<T = unknown>(path: string, init: RequestInit = {}): Promise<T> {
const r = await fetch(`${BASE}${path}`, {
...init,
headers: {
"x-api-key": KEY,
"Content-Type": "application/json",
...(init.headers ?? {}),
},
});
if (!r.ok) {
const err = await r.json().catch(() => ({}));
throw new Error(`Eviav ${r.status} — ${err.error ?? r.statusText} [${err.request_id ?? "?"}]`);
}
return r.json() as Promise<T>;
}
// Geocoding: dirección → coordenadas
export const geocode = (q: string) =>
eviav<{ features: Array<{ center: [number, number]; place_name: string }> }>(
`/v1/geocode?q=${encodeURIComponent(q)}`,
);
// Reverse: coordenadas → dirección
export const reverse = (lat: number, lon: number) =>
eviav(`/v1/reverse?lat=${lat}&lon=${lon}`);
// Ruta entre dos puntos
export const route = (from: [number, number], to: [number, number]) =>
eviav(`/v1/directions?coordinates=${from.join(",")};${to.join(",")}&steps=true`);
// POIs por categoría cerca de un punto
export const places = (lat: number, lon: number, category: string, radius = 1000) =>
eviav(`/v1/places?lat=${lat}&lon=${lon}&category=${category}&radius=${radius}`);
// Optimización multi-parada (TSP)
export const optimize = (stops: Array<[number, number]>) =>
eviav("/v1/optimization", {
method: "POST",
body: JSON.stringify({ coordinates: stops, roundtrip: true }),
});Uso:
import { geocode, route } from "./lib/eviav";
const g = await geocode("Av. Amazonas, Quito");
console.log(g.features[0].center); // [-78.4836, -0.1807]
const r = await route([-78.51, -0.22], [-78.48, -0.12]);
console.log(r.routes[0].distance, "m,", r.routes[0].duration, "s");Python
SDK oficial (recomendado) — pip install eviav. Cliente sync + async sobre httpx, con retries automáticos. Python 3.10+.
from eviav import Client
client = Client(api_key="eviav_live_…")
g = client.geocode("Av. Amazonas, Quito")
print(g["results"][0]["lat"], g["results"][0]["lon"])
r = client.directions([(-78.51, -0.22), (-78.48, -0.12)], steps=True)
print(r["routes"][0]["distance"], "m,", r["routes"][0]["duration"], "s")
client.close()Async (mismo API, awaitable):
import asyncio
from eviav import AsyncClient
async def main():
async with AsyncClient(api_key="eviav_live_…") as client:
g = await client.geocode("Quito")
print(g["results"][0]["label"])
asyncio.run(main())O sin dependencias, un wrapper manual con httpx:
# eviav_api.py
import os, httpx
BASE = os.getenv("EVIAV_BASE_URL", "https://api.eviav.com")
KEY = os.environ["EVIAV_API_KEY"]
HEADERS = {"x-api-key": KEY}
client = httpx.Client(base_url=BASE, headers=HEADERS, timeout=20.0)
def _get(path: str, **params):
r = client.get(path, params=params)
r.raise_for_status()
return r.json()
def _post(path: str, body: dict):
r = client.post(path, json=body)
r.raise_for_status()
return r.json()
def geocode(q: str):
return _get("/v1/geocode", q=q)
def reverse(lat: float, lon: float):
return _get("/v1/reverse", lat=lat, lon=lon)
def route(from_, to_):
coords = f"{from_[0]},{from_[1]};{to_[0]},{to_[1]}"
return _get("/v1/directions", coordinates=coords, steps="true")
def places(lat: float, lon: float, category: str, radius: int = 1000):
return _get("/v1/places", lat=lat, lon=lon, category=category, radius=radius)
def optimize(stops):
return _post("/v1/optimization", {"coordinates": stops, "roundtrip": True})Uso:
import eviav_api as eviav
g = eviav.geocode("Av. Amazonas, Quito")
print(g["results"][0]["lat"], g["results"][0]["lon"])
r = eviav.route([-78.51, -0.22], [-78.48, -0.12])
print(r["routes"][0]["distance"], "m,", r["routes"][0]["duration"], "s")Browser — mapa interactivo con MapLibre
SDK oficial (recomendado) — npm install @eviav/maps maplibre-gl. Envuelve MapLibre GL JS con auth, estilo Eviav y helpers; inyecta la API key por ti.
import { EviavMap } from "@eviav/maps";
import "maplibre-gl/dist/maplibre-gl.css";
const map = new EviavMap({
container: "map",
// ⚠ Usá una key con scope SOLO 'tiles' para exponer en browser.
apiKey: process.env.NEXT_PUBLIC_EVIAV_TILES_KEY,
style: "streets", // streets | dark | satellite | terrain | hybrid
center: [-78.48, -0.18], // [lon, lat] (igual que MapLibre)
zoom: 12,
});
map.addEviavMarkers([{ lon: -78.48, lat: -0.18, label: "Quito" }]);⚠ El contenedor del mapa DEBE tener altura (ej. #map { height: 400px }). Sin altura, MapLibre renderiza un canvas de 0px y verás la pantalla en blanco — es el error nº1 de los que arrancan.
Ejemplo completo (copiar y pegar) — guárdalo como un .html y ábrelo en el navegador (sin bundler, vía esm.sh). Reemplaza TU_API_KEY por una key con scope 'tiles'.
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://esm.sh/maplibre-gl@5/dist/maplibre-gl.css" rel="stylesheet" />
<style>html,body,#map{margin:0;height:100%}</style>
</head>
<body>
<div id="map"></div>
<script type="module">
import { EviavMap } from "https://esm.sh/@eviav/[email protected]";
const map = new EviavMap({
container: "map",
apiKey: "TU_API_KEY", // key con scope 'tiles'
style: "streets", // streets | dark | satellite | terrain | hybrid
center: [-78.48, -0.18], // [lon, lat]
zoom: 12,
});
map.addEviavMarkers([{ lon: -78.48, lat: -0.18, label: "Quito" }]);
// La atribución OSM/Eviav (requerida) se agrega sola.
</script>
</body>
</html>¿De dónde sale la API key? Regístrate y créala en /dashboard/api-keys (el tier 'test' da 10k req/mes sin cargo). Para chips glass y pines de marca, importa también el CSS: import "@eviav/maps/glass.css" (o llama injectEviavStyles()). Ojo: addEviavRoute/showTraffic usan otros servicios — la key necesita además scope routing/traffic, no solo 'tiles'.
¿Usas React? npm install @eviav/maps-react trae <EviavMap> + hooks (useEviavGeocoder, useEviavRoute). Ver React.
O con MapLibre crudo (sin el wrapper), apuntando al style autenticado:
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
const map = new maplibregl.Map({
container: "map",
style: `https://api.eviav.com/v1/tiles/streets/style.json?apikey=${process.env.NEXT_PUBLIC_EVIAV_TILES_KEY}`,
center: [-78.48, -0.18],
zoom: 12,
});Dibujo de geocercas — @eviav/maps-draw
Paquete aparte (no infla @eviav/maps): dibuja y edita polígonos, líneas y puntos sobre el mapa — como mapbox-gl-draw, sin dependencias externas. El caso típico: el usuario dibuja una zona y la mandás a POST /v1/geofences (la geometría que entrega ya es un Polygon GeoJSON válido).
npm install @eviav/maps-draw maplibre-glimport { EviavMap } from "@eviav/maps";
import { EviavDraw } from "@eviav/maps-draw";
const map = new EviavMap({ container: "map", apiKey: "TU_API_KEY" });
const draw = new EviavDraw(map);
draw.changeMode("draw_polygon");
draw.on("create", async (feature) => {
// feature.geometry: { type: "Polygon", coordinates: [...] }
await fetch("https://api.eviav.com/v1/geofences", {
method: "POST",
headers: { "content-type": "application/json", "x-api-key": "TU_API_KEY" },
body: JSON.stringify({ name: "Zona de reparto", geometry: feature.geometry }),
});
});Modo simple_select (default): clic selecciona una figura, arrastra un vértice para moverlo, arrastra un punto medio para insertar uno nuevo, arrastra el cuerpo para trasladar la figura entera, Supr/Backspace la borra. Modos draw_polygon/draw_line/draw_point: clic para marcar puntos, doble clic o Enter termina, clic sobre el primer punto cierra el polígono (como Google Maps).
React
npm install @eviav/maps-react @eviav/maps maplibre-gl. Componente <EviavMap> SSR-safe + hooks. Funciona con Next.js, Remix, etc.
import { EviavMap, useEviavGeocoder } from "@eviav/maps-react";
import "maplibre-gl/dist/maplibre-gl.css";
export function App() {
return (
<EviavMap
apiKey={process.env.NEXT_PUBLIC_EVIAV_TILES_KEY!}
center={[-78.48, -0.18]}
zoom={12}
height={500}
/>
);
}
// Hook de autocompletado con debounce:
function AddressSearch({ apiKey }: { apiKey: string }) {
const [q, setQ] = useState("");
const { results, loading } = useEviavGeocoder(q, { apiKey, lat: -0.18, lon: -78.48 });
return (
<>
<input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Buscar dirección…" />
<ul>{results.map((r) => <li key={r.place_id}>{r.label}</li>)}</ul>
</>
);
}@eviav/maps-react 0.2.0 incluye además useEviavRoute, useEviavStreetView (imagen street-level más cercana) y el componente <StreetViewer> (visor 360, con Pannellum como peer opcional). Todos los métodos de @eviav/maps (addGlassPin, enable3DBuildings, setTheme…) están disponibles sobre la instancia que devuelve useEviavMap().
import { useEviavStreetView, StreetViewer } from "@eviav/maps-react";
function StreetView({ apiKey, lat, lon }: { apiKey: string; lat: number; lon: number }) {
const { url, isPano } = useEviavStreetView(lat, lon, { apiKey });
if (!url) return null;
return isPano ? <StreetViewer url={url} /> : <img src={url} alt="street view" />;
}Flutter — eviav_maps (mapa nativo)
eviav_maps es el lienzo de mapa nativo de Eviav para Flutter: el MISMO motor que usa la app de primera parte. Envuelve MapLibre con el estilo oficial, el puck canon, el glow de ruta, los reportes (semáforos, radares, controles, accidentes, vías cerradas y zonas) y la cámara POV 3D de navegación. Requiere tu API key de Eviav (modelo Mapbox): el estilo, los tiles, las fuentes y los sprites se sirven por el gateway medido api.eviav.com con tu key — el uso se mide y factura. Consíguela en el dashboard.
# pubspec.yaml
dependencies:
eviav_maps: ^0.3.0
maplibre_gl: ^0.26.1 # peer del render: importa LatLng/CameraPosition directoEl widget central, EviavMapView, recibe el style.json ya resuelto y entrega un EviavMapController imperativo. Todo el render del mapa (ruta, puck, reportes, cámara, proyección) pasa por ese controlador.
import 'package:flutter/material.dart';
import 'package:eviav_maps/eviav_maps.dart';
// El barrel NO re-exporta maplibre_gl: importa los tipos nativos directo.
import 'package:maplibre_gl/maplibre_gl.dart';
class MapPage extends StatefulWidget {
const MapPage({super.key});
@override
State<MapPage> createState() => _MapPageState();
}
class _MapPageState extends State<MapPage> {
// Tu API key de Eviav (dashboard). Pásala con --dart-define=EVIAV_API_KEY=...
static const _apiKey = String.fromEnvironment('EVIAV_API_KEY');
final _map = EviavMapController();
String? _style;
@override
void initState() {
super.initState();
// Estilo oficial con TU API key, contra el gateway medido (día/noche por hora local), con reintentos.
EviavMapStyle.forApiKey(apiKey: _apiKey, dark: eviavWantsDarkNow()).then((s) {
_map.parseRouteModeStyle(s);
setState(() => _style = s);
});
}
Future<void> _onStyleLoaded() async {
// Ruta de navegación + puck + cámara POV, todo del paquete.
const path = <List<double>>[[-78.4678, -0.1807], [-78.4651, -0.1797], [-78.4630, -0.1779]];
await _map.drawNavRoute(coords: path, altFeats: const [], bandFeats: const []);
await _map.setNavPuck(lat: -0.1797, lon: -78.4651, bearing: 35);
await _map.smoothCameraTo(const CameraPosition(
target: LatLng(-0.1797, -78.4651), zoom: 16.8, tilt: 55, bearing: 35));
}
@override
Widget build(BuildContext context) {
final style = _style;
if (style == null) return const Center(child: CircularProgressIndicator());
return EviavMapView(
styleString: style,
controller: _map,
onMapCreated: (_) {},
onStyleLoaded: _onStyleLoaded,
onMapClick: (point, latLng) {/* ... */},
onCameraIdle: () {/* reproyecta overlays */},
);
}
}EviavMapController — API del lienzo, agrupada:
// Estilo / tema
EviavMapStyle.forApiKey({required apiKey, bool dark}); // estilo oficial con tu key (gateway medido, reintentos)
eviavWantsDarkNow(); // bool — día/noche por hora local
ctrl.parseRouteModeStyle(json); // lee color de vías + guarda originales
ctrl.applyRouteMode(true); // modo Cómo llegar: atenúa vías + oculta POIs
// Cámara
ctrl.smoothCameraTo(goal, duration: ...); // tween POV ease-out (target/zoom/tilt/bearing)
ctrl.frameBounds(bounds, viewportWidth: w, viewportHeight: h, bandTopPx: 110, bandBottomPx: b);
ctrl.cancelCameraTween();
// Proyección geo→pantalla (corrige el DPR físico de Android)
await ctrl.screenLocation(latLng); // para anclar overlays Flutter
await ctrl.screenLocationBatch(points);
// Ruta de navegación + puck
ctrl.drawNavRoute(coords: ..., altFeats: ..., bandFeats: ...);
ctrl.setNavTraveled(gray: ..., fade: ...); // estela recorrida + degradado bajo el puck
ctrl.setNavPuck(lat: ..., lon: ..., bearing: ...);
ctrl.liftNavPuck(); ctrl.hideNavPuck(); ctrl.clearNav();
// Ruta de planificación (Cómo llegar)
ctrl.drawPlanRoute(altFeats: ..., coords: ..., animate: true);
ctrl.animatePlanRoute(coords); ctrl.clearRoute();
// Reportes (capas symbol / círculo / línea + zonas raster)
ctrl.ensureImage(id, bytes); // registra un ícono (idempotente)
ctrl.renderSymbolPoints(...); ctrl.renderCircleFeatures(...);
ctrl.renderLineFeatures(... lineBlur: ...); // líneas (con desenfoque opcional)
ctrl.renderZoneRaster(... quad: ...); // polígono difuminado (image-source)
ctrl.clearFeatureSource(id); ctrl.removeFeatureLayers(id, layers);Para los reportes, el paquete trae helpers: eviavBuildTrafficLightImage() (ícono canon del semáforo, sin assets), eviavBuildZoneRaster(ring, bbox, rgb) (polígono → PNG difuminado) y los constructores de features eviavPointFeature / eviavLineStringFeature. Tus íconos SVG propios se rasterizan en la app y se pasan a ensureImage.
Widgets reutilizables incluidos: EviavLocationPuck, EviavGlassPin (pin teardrop glass), EviavStreetCallout, MapCredits (atribución requerida) y los overlays de POI / marca / lugares en vivo.
El paquete incluye una app de ejemplo standalone (example/) que renderiza el mapa completo de Eviav dependiendo solo de eviav_maps, más una suite de tests (flutter test). El estilo, los tiles, las fuentes y los sprites se sirven por el gateway medido con tu API key (pásala al example con --dart-define=EVIAV_API_KEY=...). La atribución (MapCredits) es obligatoria — ver la sección Atribución.
Casos de uso comunes
Dibujar una ruta en el mapa
const r = await route([-78.51, -0.22], [-78.48, -0.12]);
const geometry = r.routes[0].geometry; // GeoJSON LineString
map.addSource("route", { type: "geojson", data: { type: "Feature", geometry, properties: {} } });
map.addLayer({
id: "route-line",
type: "line",
source: "route",
paint: { "line-color": "#FF6A1A", "line-width": 4 },
});Autocompletado de direcciones (input con debounce)
let timer: ReturnType<typeof setTimeout> | null = null;
input.addEventListener("input", (e) => {
const q = (e.target as HTMLInputElement).value;
if (timer) clearTimeout(timer);
timer = setTimeout(async () => {
if (q.length < 3) return;
const r = await fetch(`/api/search?q=${encodeURIComponent(q)}`); // proxy backend
const { features } = await r.json();
renderSuggestions(features);
}, 200);
});
// Backend (Next.js / Express / Fastify):
// app/api/search/route.ts
export async function GET(req: Request) {
const q = new URL(req.url).searchParams.get("q") ?? "";
const r = await fetch(
`https://api.eviav.com/v1/search?text=${encodeURIComponent(q)}&lat=-0.18&lon=-78.48`,
{ headers: { "x-api-key": process.env.EVIAV_API_KEY! } },
);
return new Response(await r.text(), { status: r.status, headers: { "content-type": "application/json" } });
}Optimización de flota (delivery, técnicos en ruta)
const result = await fetch("https://api.eviav.com/v1/fleet", {
method: "POST",
headers: { "x-api-key": process.env.EVIAV_API_KEY!, "Content-Type": "application/json" },
body: JSON.stringify({
vehicles: [
{ id: 1, start: [-78.51, -0.22], end: [-78.51, -0.22], capacity: [50], time_window: [28800, 64800] },
{ id: 2, start: [-78.50, -0.20], end: [-78.50, -0.20], capacity: [50], time_window: [28800, 64800] },
],
jobs: [
{ id: 1, location: [-78.48, -0.12], amount: [5], time_windows: [[32400, 43200]] },
{ id: 2, location: [-78.49, -0.15], amount: [8], time_windows: [[36000, 50400]] },
{ id: 3, location: [-78.47, -0.10], amount: [3] },
],
}),
}).then(r => r.json());
console.log(result.routes); // ruta optimizada por vehículoIsócronas — áreas de cobertura por tiempo
// "¿Qué área cubro en 10, 20 y 30 minutos en auto desde aquí?"
const r = await fetch(
"https://api.eviav.com/v1/isochrone?lat=-0.18&lon=-78.48&contours=10,20,30&costing=auto",
{ headers: { "x-api-key": process.env.EVIAV_API_KEY! } },
);
const iso = await r.json(); // FeatureCollection con 3 polígonosNavegación con voz (turn-by-turn para TTS)
Agrega voice=true a /v1/directions para recibir narrativa turn-by-turn con instrucciones de voz listas para sintetizar (TTS) en tu app — 'Continúa 200 m y luego gira a la derecha'. Idioma con language (BCP-47, ej es-ES o en-US).
curl "https://api.eviav.com/v1/directions?coordinates=-0.18,-78.48;-0.12,-78.45&voice=true&language=es-ES" \
-H "x-api-key: TU_KEY"
# → routes[].legs[].steps[] = { instruction, voice_instruction, voice_after, type, street_names, distance_m, duration_s }ETAs con tráfico (departure_time)
Pasá departure_time (ISO 8601 o epoch ms) a /v1/directions o /v1/matrix y la duración se ajusta con el tráfico típico de esa hora (probe-data agregada). Sin el parámetro, devuelve flujo libre. La respuesta incluye un objeto traffic con el factor aplicado.
curl "https://api.eviav.com/v1/directions?coordinates=-0.22,-78.51;-0.12,-78.48&departure_time=2026-06-01T18:00:00Z" \
-H "x-api-key: TU_KEY"
# → { routes: [...], traffic: { applied: true, factor: 1.6, samples, departure_time } }Places enriquecidas (datos OSM)
Cada resultado de /v1/places trae un objeto details con datos de Eviav: opening_hours, phone, website, cuisine, brand, wheelchair, shop y tourism (los campos ausentes vienen null). Útil para tarjetas de POI más ricas en el autocomplete.
curl "https://api.eviav.com/v1/places?lat=-0.18&lon=-78.48&category=restaurant&radius=800" \
-H "x-api-key: TU_KEY"
# → results[].details = { opening_hours, phone, website, cuisine, brand, wheelchair, shop, tourism }Mapas avanzados: 3D, satélite, heatmaps y dark mode
El SDK @eviav/maps (y @eviav/maps-react) envuelve MapLibre GL e incluye, sin configuración extra:
- Estilos streets, satellite, terrain e hybrid — más dark mode y un editor de estilos.
- Edificios 3D, terreno con relieve, cielo/luz y cámara cinematográfica.
- Capas de visualización deck.gl: heatmaps, hexágonos, arcos y trayectorias.
- Imágenes 360° a nivel de calle (visor integrado).
- Street View: GET /v1/streetview?lat=&lon= devuelve las imágenes de calle más cercanas (Mapillary, datos abiertos), con flag is_pano para el visor 360.
- Mapas offline: packs PMTiles regionales descargables (GET /v1/tiles/offline) para usar sin conexión con MapLibre.
- Capa de tráfico estilo Google: map.showTraffic() pinta las calles por congestión (verde/amarillo/rojo) desde GET /v1/traffic.
import { EviavMap } from "@eviav/maps";
import "@eviav/maps/glass.css"; // estilos de los pines/chips glass de marca
const map = new EviavMap({
container: "map",
apiKey: process.env.NEXT_PUBLIC_EVIAV_TILES_KEY,
style: "streets", // streets | dark | satellite | terrain | hybrid
});
// Componentes de marca Eviav (el look de eviav.com):
map.addGlassPin(-78.48, -0.18, { title: "Quito", color: "#E85410" });
map.on("load", () => {
map.enable3DBuildings(); // inclina la cámara → edificios 3D extruidos
map.setTimeOfDay(18); // luz cálida del atardecer sobre el 3D
});
map.setTheme("dark"); // cambia de tema preservando la cámara
map.showTraffic(); // tráfico por congestión (verde/amarillo/rojo)
await map.addEviavRoute([[-78.51, -0.22], [-78.48, -0.12]]); // ruta glass
// Heatmaps/densidad (deck.gl, peer OPCIONAL):
// npm i @deck.gl/mapbox @deck.gl/aggregation-layers
await map.addHeatmap(points); // points: [lon, lat][]@eviav/maps (0.5.0) incluye estos métodos. Los heatmaps/densidad usan deck.gl como peer opcional (instálalo solo si los necesitas — no infla el bundle base). Street View y offline son endpoints del API (GET /v1/streetview, /v1/tiles/offline). Demo en vivo en /map.
POI brandeados (chips): desde @eviav/maps 0.5.0, en estilos vectoriales (streets/dark) los POI se dibujan automáticamente como chips glass con el ícono y color de categoría de Eviav + el nombre — idéntico a eviav.com, sin tocar tu código. Importá la hoja de estilos del SDK y, si no los querés, pasá poiChips:false.
import { EviavMap } from "@eviav/maps";
import "@eviav/maps/glass.css"; // chips + pines glass
new EviavMap({ apiKey: KEY, style: "streets" }); // chips brandeados automáticos
// new EviavMap({ apiKey: KEY, style: "streets", poiChips: false }); // desactivarEviav Studio: personaliza el mapa de cada proyecto
Desde el dashboard puedes personalizar el mapa de cada proyecto (cada API key): colores por rol (tierra, agua, vías, edificios…), edificios 3D, y sumar tus propios datos como capas (tus tilesets subidos en Tilesets, como puntos/líneas/áreas). Lo ves en vivo y publicas.
Consumo automático por key (modelo Mapbox): cuando publicas, la app que usa esa API key con style: "streets" (o "dark") recibe el mapa custom de ESE proyecto — sin cambiar una línea de código. Un cliente con 10 keys personaliza 10 mapas.
// Tu app no cambia: usas tu key de SIEMPRE con el estilo de siempre,
// y recibe el mapa que personalizaste para ESE proyecto en el dashboard.
new EviavMap({ apiKey: PROJECT_KEY, style: "streets" });
// Alternativa explícita (por URL del estilo):
// new EviavMap({ apiKey, style: "https://api.eviav.com/v1/tiles/style/<id>/style.json" });Editor en /dashboard/estilos. Tu estilo custom se sirve cerrado y key-gated, igual que el mapa base.
Opciones del editor: colores por rol, tipografía (familia, peso, tamaño, interletrado, halo, idioma de nombres), grosor de vías, densidad y categorías de POI (comida, transporte, comercios, salud…), toggles de capas (POI, etiquetas, límites, edificios 2D/3D, parques, vías, agua, aeropuertos) y vista inicial (centro/zoom/inclinación/rotación).
Distinto en web vs app (por plataforma): en el editor defines overrides por plataforma. Tu app pide el estilo con ?platform=web o ?platform=app y recibe esos ajustes encima de la config General — p. ej. etiquetas más grandes y otra vista inicial en mobile.
// Web: config General
new EviavMap({ apiKey: KEY, style: "streets" });
// App (mobile): recibe los overrides de la plataforma "app"
// styleString: "https://api.eviav.com/v1/tiles/streets/style.json?apikey=KEY&platform=app"Tipografía: familias incluidas y tu propia fuente
En Eviav Studio puedes elegir la tipografía del mapa entre una lista amplia de familias (sans, redondeadas, serif, display y monoespaciadas — Figtree es la de Eviav por defecto), además del tamaño y peso de las etiquetas, el idioma de los nombres, el grosor de las vías, la densidad de POIs y qué capas se muestran. Todo se aplica solo a la app que usa esa API key.
¿Quieres tu propia fuente de marca? No la subes a Eviav: tú mismo alojas los glyphs y apuntas el estilo a tu URL con el parámetro glyphs (igual que hace Mapbox). Tu fuente nunca pasa por nuestros servidores.
- Genera los glyphs PBF desde tu .ttf/.otf (una vez):
npx @maplibre/font-maker ./MiFuente.ttf ./glyphs— produce carpetas{fontstack}/{range}.pbf. - Nombra las carpetas igual que los font-stacks del estilo Eviav, para que tu fuente reemplace a cada peso:
Figtree Regular,Figtree Medium,Figtree SemiBold,Figtree Bold,Figtree ExtraBold,Figtree Italic(o el nombre de la familia que hayas elegido en Studio). - Alójalos en cualquier CDN/servidor estático con CORS abierto.
- Apunta el estilo a tu URL de glyphs (cualquier lenguaje, porque es solo la URL del estilo):
// El gateway valida que glyphs sea https con {fontstack} y {range}.
const GLYPHS = "https://glyphs.tuempresa.com/{fontstack}/{range}.pbf";
// 1) Browser / @eviav/maps (JS): pasas la URL del estilo con ?glyphs=
new EviavMap({
apiKey: PROJECT_KEY,
style: `https://api.eviav.com/v1/tiles/streets/style.json?apikey=${PROJECT_KEY}&glyphs=${encodeURIComponent(GLYPHS)}`,
});
// 2) Flutter (paquete eviav / maplibre): mismo string de estilo
// MaplibreMap(styleString: "https://api.eviav.com/v1/tiles/streets/style.json?apikey=$key&glyphs=...")
// 3) MapLibre GL JS crudo: o pegas la URL como arriba, o en runtime
map.setGlyphs(GLYPHS);Lo más simple es elegir una familia incluida en Studio (sin generar nada). La fuente propia es para clientes que necesitan su tipografía corporativa exacta.
Apps nativas: cerrar el mapa con atestación
Eviav Maps es un servicio cerrado (modelo Google/Mapbox): los tiles, glyphs y sprites se consumen vía la API, no se descargan. En el navegador, el SDK los pide con tu API key. En una app NATIVA, embeber una API key sería un riesgo (no es revocable sin actualizar a millones de usuarios), así que Eviav usa un token EFÍMERO de mapa, emitido solo a dispositivos reales mediante atestación: App Attest (iOS) y Play Integrity (Android). El mapa de la app de primera parte ya implementa este flujo; aquí está cómo funciona si construyes tu propio cliente nativo.
// Flujo de atestación → token efímero de mapa (TTL ~1h, se refresca)
// 1) Pedir un reto de un solo uso
const { challenge } = await fetch("https://eviav.com/api/maps-challenge", {
method: "POST",
}).then((r) => r.json());
// 2) Atestar el dispositivo con la API nativa de la plataforma
// iOS: DCAppAttestService (App Attest) → { keyId, attestation } | { credential, assertion }
// Android: StandardIntegrityManager (Play Integrity) → { integrityToken }
// 3) Canjear la atestación por un token de mapa
const body = Platform.isIOS
? { platform: "ios", action: "attest", challenge, keyId, attestation }
: { platform: "android", challenge, integrityToken };
const { token } = await fetch("https://eviav.com/api/maps-token", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(body),
}).then((r) => r.json());
// 4) Mandar el token en CADA request de tiles/glyphs/sprites (header global del SDK de mapas)
// Authorization: Bearer <token>
// → eviav.com/api/tiles | /api/fonts | /api/sprites (primera parte, sin API key embebida)El SDK web (browser) NO usa atestación — los navegadores no pueden atestar; ahí el cierre es por API key + origen. La atestación es exclusiva de apps nativas. Si necesitas habilitarla para tu app, escríbenos a [email protected] (requiere registrar tu bundle iOS y package Android).
Manejo de errores con retry exponencial
async function withRetry<T>(fn: () => Promise<T>, retries = 3): Promise<T> {
for (let i = 0; i < retries; i++) {
try { return await fn(); }
catch (err: any) {
// Respeta Retry-After si vino del 429, sino backoff exponencial.
if (i === retries - 1) throw err;
const wait = (err.retryAfter ?? Math.pow(2, i)) * 1000;
await new Promise(r => setTimeout(r, wait));
}
}
throw new Error("unreachable");
}
// Uso:
const data = await withRetry(() => geocode("Av. Amazonas, Quito"));Toda response trae los headers X-Request-Id,X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset y, en caso de 429, Retry-After. Si tienes que reportar un bug a soporte, incluye siempre el X-Request-Id.
Errores
Las respuestas con error siguen una forma consistente:
{
"error": "Mensaje legible para humanos",
"code": "rate_limited",
"request_id": "req_AbCdEf1234567890"
}| HTTP | code | Significado |
|---|---|---|
| 400 | bad_request | Parámetros inválidos o faltantes. |
| 401 | unauthorized | API key inválida, revocada o ausente. |
| 402 | payment_required | Sin tarjeta cargada (gate de billing). Carga una en /dashboard/facturacion. |
| 403 | scope_denied | La key no tiene scope para este endpoint. |
| 404 | not_found | Recurso no existe (tileset, dataset, etc.). |
| 429 | rate_limited | Rate limit excedido (ver headers Retry-After). |
| 500 | internal_error | Error interno — reintenta con backoff exponencial. |
| 503 | service_unavailable | Mantenimiento o upstream caído. Reintenta. |
Rate limits
Cada API key tiene un límite de requests por segundo y por minuto. El gateway responde 429 Too Many Requests cuando se excede, con headers estándar:
X-RateLimit-Limit: 1000 # max requests por ventana
X-RateLimit-Remaining: 273 # cuántas quedan
X-RateLimit-Reset: 1748394812 # epoch UTC del reset
Retry-After: 7 # segundos hasta poder reintentarLos servicios pesados (Matrix, Isochrone, Optimization, Fleet) consumen más del budget por request — Matrix cuenta 2x, Isochrone 4x, Optimization/Fleet 5x. Esto se refleja solo en el rate limit, no en la facturación (que va por request crudo).
Precios y free tier
Pago por uso: solo facturas lo que consumes por encima del free tier de cada servicio. Sin cuota fija, sin mínimos, sin permanencia.
| Servicio | Free tier / mes | USD / 1 000 req |
|---|---|---|
| Búsqueda | ||
| Geocoding | 100.000 | $0,38 |
| Reverse Geocoding | 100.000 | $0,38 |
| Search & Autofill | 50.000 | $0,50 |
| Places | 30.000 | $1,00 |
| Ruteo | ||
| Directions | 100.000 | $1,00 |
| Matrix | 100.000 | $1,00 |
| Optimization | 100.000 | $1,00 |
| Fleet Routing | 10.000 | $2,50 |
| Map Matching | 100.000 | $1,00 |
| Nearest | 100.000 | $1,00 |
| Mapas | ||
| Static Maps | 50.000 | $0,50 |
| Vector & Raster Tiles | 200.000 | $0,13 |
| Datos geográficos | ||
| Isochrone | 100.000 | $1,00 |
| Elevation | 50.000 | $1,00 |
| Timezone | 50.000 | $0,50 |
| Tile Query | 50.000 | $0,50 |
| Geofencing | 50.000 | $0,40 |
| Inteligencia artificial | ||
| Maps Grounding | 5.000 | $5,00 |
| AI Assistant | 2.000 | $5,00 |
| Tilesets propios | ||
| Custom Tilesets | 200.000 | $0,13 |
Precios sin IVA. Refresh de free tier el primer día de cada mes UTC. Volúmenes altos (>500k req/mes en un SKU) tienen descuentos automáticos — contáctanos en [email protected].
Referencia de servicios
18 endpoints REST agrupados en 6 familias. Cada uno con su método, path, scope, free tier, tarifa y ejemplo curl.
Búsqueda
Geocoding, autocompletado y búsqueda de lugares por nombre o dirección.
Geocoding
Convierte texto (dirección o lugar) en coordenadas.
curl "https://api.eviav.com/v1/geocode?q=Av.%20Amazonas%20Quito" -H "x-api-key: TU_KEY"Reverse Geocoding
Convierte coordenadas en la dirección o lugar más cercano.
curl "https://api.eviav.com/v1/reverse?lat=-0.18&lon=-78.48" -H "x-api-key: TU_KEY"Search & Autofill
Autocompletado predictivo de lugares y direcciones con sesgo por ubicación.
curl "https://api.eviav.com/v1/search?text=Av%20Amaz&lat=-0.18&lon=-78.48" -H "x-api-key: TU_KEY"Places
POIs por categoría cerca de un punto y agregaciones de densidad.
curl "https://api.eviav.com/v1/places?lat=-0.18&lon=-78.48&category=restaurant&radius=1000" -H "x-api-key: TU_KEY"Ruteo
Direcciones, matriz, optimización y emparejamiento de trazas GPS.
Directions
Ruta entre 2+ puntos con distancia, duración, geometría y steps.
curl "https://api.eviav.com/v1/directions?coordinates=-0.22,-78.51;-0.12,-78.48&steps=true" -H "x-api-key: TU_KEY"Matrix
Matriz de tiempos y distancias entre orígenes y destinos (máx 2 500 elementos).
curl "https://api.eviav.com/v1/matrix?coordinates=-0.22,-78.51;-0.12,-78.48" -H "x-api-key: TU_KEY"Optimization
Ordena varias paradas en el recorrido más eficiente (TSP single-vehicle).
curl -X POST "https://api.eviav.com/v1/optimization" -H "x-api-key: TU_KEY" -H "Content-Type: application/json" -d '{"coordinates":[[-0.22,-78.51],[-0.12,-78.48],[-0.18,-78.49]],"roundtrip":true}'Fleet Routing
Optimización de flota: múltiples vehículos + trabajos con restricciones (VROOM).
curl -X POST "https://api.eviav.com/v1/fleet" -H "x-api-key: TU_KEY" -H "Content-Type: application/json" -d '{"vehicles":[{"id":1,"start":[-78.51,-0.22],"end":[-78.51,-0.22]}],"jobs":[{"id":1,"location":[-78.48,-0.12]}]}'Map Matching
Ajusta una traza GPS ruidosa a la red vial real (snap-to-road).
curl "https://api.eviav.com/v1/match?coordinates=-0.18,-78.48;-0.181,-78.481;-0.182,-78.482" -H "x-api-key: TU_KEY"Nearest
Encuentra la o las vías más cercanas a un punto en la red vial.
curl "https://api.eviav.com/v1/nearest?point=-0.18,-78.48&number=1" -H "x-api-key: TU_KEY"Mapas
Tiles vectoriales/raster y mapas estáticos renderizados con el estilo Eviav.
Static Maps
Imagen PNG renderizada con el estilo Eviav (center+zoom o bbox, markers, path).
curl "https://api.eviav.com/v1/static?center=-0.18,-78.48&zoom=13&width=600&height=400" -H "x-api-key: TU_KEY" -o mapa.pngVector & Raster Tiles
Tiles autenticados de streets (vector), satellite y terrain.
curl "https://api.eviav.com/v1/tiles/streets/12/1155/2050.pbf" -H "x-api-key: TU_KEY"Datos geográficos
Elevación, zona horaria, isócronas y consultas espaciales sobre POIs.
Isochrone
Áreas alcanzables en X minutos (polígonos GeoJSON) con Valhalla.
curl "https://api.eviav.com/v1/isochrone?lat=-0.18&lon=-78.48&contours=10,20&costing=auto" -H "x-api-key: TU_KEY"Elevation
Elevación en metros sobre el nivel del mar (DEM terrain-rgb) para una coordenada.
curl "https://api.eviav.com/v1/elevation?lat=-0.18&lon=-78.48" -H "x-api-key: TU_KEY"Timezone
Zona horaria IANA y offset UTC para una coordenada.
curl "https://api.eviav.com/v1/timezone?lat=-0.18&lon=-78.48" -H "x-api-key: TU_KEY"Tile Query
Features OSM cercanos a una coordenada, con sus propiedades.
curl "https://api.eviav.com/v1/tilequery?lat=-0.18&lon=-78.48&radius=50" -H "x-api-key: TU_KEY"Geofencing
Geocercas por organización + check point-in-fence. Gestión gratis; se factura el check.
curl "https://api.eviav.com/v1/geofences/check?lat=-0.18&lon=-78.48" -H "x-api-key: TU_KEY"Inteligencia artificial
Respuestas en lenguaje natural fundamentadas con datos del mapa.
Maps Grounding
Respuesta en lenguaje natural fundamentada con datos del mapa.
curl -X POST "https://api.eviav.com/v1/grounding" -H "x-api-key: TU_KEY" -H "Content-Type: application/json" -d '{"query":"cafeterías cerca de la Plaza Foch"}'AI Assistant
Chat geoespacial, planificador de viajes y reconocimiento visual (foto → lugar).
curl -X POST "https://api.eviav.com/v1/ai/chat" -H "x-api-key: TU_KEY" -H "Content-Type: application/json" -d '{"query":"¿Qué hago un día en Quito?","lat":-0.18,"lon":-78.48}'Tilesets propios
Hospedaje y servido privado de tus tilesets self-serve.
Custom Tilesets
Sirve vector tiles de tus tilesets self-serve (privados por organización).
curl "https://api.eviav.com/v1/tiles/custom/ts_ABC/12/1155/2050.pbf" -H "x-api-key: TU_KEY"Más endpoints
Además de los servicios de la tabla anterior, la API expone estos endpoints. La referencia completa de parámetros y respuestas está en el spec OpenAPI y puedes probarlos en el Playground.
Inteligencia artificial
IA geoespacial fundamentada en datos del mapa (no alucina lugares). Tres endpoints:
POST /v1/grounding— respuesta en lenguaje natural fundamentada (la del catálogo de IA).POST /v1/ai/chat— chat geoespacial con contexto de ubicación (lat/lon opcionales).POST /v1/ai/trip— planificador de viajes: dado un conjunto de lugares, arma un itinerario.POST /v1/ai/vision— reconocimiento visual: identifica un lugar a partir de una foto (imagen en base64).
curl -X POST "https://api.eviav.com/v1/ai/chat" \
-H "x-api-key: TU_KEY" -H "Content-Type: application/json" \
-d '{"query":"¿Qué hago un día en Quito?","lat":-0.18,"lon":-78.48}'Geocercas (gestión)
Además del check facturable (/v1/geofences/check), la gestión de geocercas es gratuita:
POST /v1/geofences— crear una geocerca (nombre + polígono GeoJSON).GET /v1/geofences— listar las geocercas de tu organización.DELETE /v1/geofences/{id}— eliminar una geocerca.
Tráfico, Street View y mapas offline
GET /v1/traffic?bbox=…— congestión por segmento (GeoJSON) para pintar el mapa estilo Google. Gratis (beta).POST /v1/telemetry— ingesta de trazas GPS anonimizadas que alimentan el tráfico (probe-data, modelo Mapbox). Gratis.GET /v1/streetview?lat=&lon=— imágenes a nivel de calle más cercanas (Mapillary), con flag is_pano para el visor 360. Gratis (beta).GET /v1/tiles/offline— catálogo de packs PMTiles regionales descargables para uso sin conexión. Gratis.GET /v1/tiles— discovery: lista los estilos y tilesets disponibles. Gratis.
Variantes de endpoints existentes
Algunas rutas son variantes que facturan bajo el mismo SKU de su familia:
GET /v1/route— ruta simple entre 2 puntos (versión reducida de /v1/directions). Factura como directions.GET /v1/autofill— búsqueda con componentes de dirección desglosados. Factura como search.POST /v1/geocode/batch— geocoding de hasta 100 consultas en una llamada. Factura como geocode (por consulta).GET /v1/places/aggregate— densidad de POIs por categoría en un bbox. Factura como places.
Convenciones de respuesta
El formato de respuesta varía según la familia del endpoint. Es intencional y consistente dentro de cada familia:
/v1/geocode,/v1/reverse— objeto plano por resultado: { name, lat, lon, type }. Pensado para geocoding simple./v1/search,/v1/autofill— incluye confidence (0–1) para rankear y properties anidado con los componentes de dirección (calle, ciudad, país, etc.)./v1/directions,/v1/route,/v1/matrix— estilo OSRM: distancias en metros, duraciones en segundos, geometría como GeoJSON LineString ([lon,lat])./v1/tiles/{tileset}/{z}/{x}/{y}— vector tiles (MVT/protobuf) o raster (PNG/JPG)./v1/tiles/{style}/style.json— documento de estilo MapLibre GL (spec v8).
Convenciones de coordenadas (lat,lon / [lon,lat])
Regla de oro: los parámetros y cuerpos REST de Eviav usan lat,lon (latitud primero, estilo Google/Leaflet). Cualquier dato GeoJSON (RFC 7946 — geometría en respuestas, o passthrough a VROOM) usa [lon, lat]. El orden exacto por endpoint:
| Endpoint / contexto | Orden | Ejemplo válido (Quito) |
|---|---|---|
?lat= / ?lon= — geocode, reverse, places, search, isochrone, timezone, elevation | lat, lon | ?lat=-0.18&lon=-78.48 |
center=, coordinates= — static, directions, route, matrix, match, nearest | lat, lon | coordinates=-0.18,-78.48;-0.12,-78.45 |
Cuerpo de POST /v1/optimization — coordinates[] | lat, lon | { "coordinates": [[-0.18,-78.48], …] } |
Cuerpo de POST /v1/fleet — VROOM nativo (location, start/end) | [lon, lat] | "location": [-78.48, -0.18] |
Geometría GeoJSON en respuestas — directions/route LineString, geocode feature.center | [lon, lat] | [-78.48, -0.18] |
| Respuestas geocode/reverse (campos nombrados) | campos lat/lon | { "lat": -0.18, "lon": -78.48 } |
⚠ Ejemplo INVÁLIDO: ?lat=-78.48&lon=-0.18 (invertido) — latitud -78.48 está casi en el polo sur y el punto cae en el océano, no en Quito. En params y cuerpos REST de Eviav la latitud va primero. Tolerancia: si pasás lon,lat por error y solo esa interpretación cae en una región con cobertura, Eviav la corrige automáticamente; si ambas interpretaciones son válidas, devuelve 400 pidiendo lat,lon explícito.
Estilos de mapa
streets,dark— vectoriales: capas curadas (calles, edificios, etiquetas) con paleta clara/oscura.satellite,terrain— raster: el style.json solo trae background + la capa raster porque toda la información visual viene en la imagen. Es lo esperado, no un estilo incompleto.hybrid— satélite de base + etiquetas y calles vectoriales encima.
Discovery: GET /v1/tiles lista los estilos y tilesets disponibles. ¿Proxeas los tiles? Pide el style.json con ?embed_key=false para obtener URLs sin la API key.
Niveles de zoom y overzoom
Los tiles vectoriales (streets/dark) se generan hasta zoom 14 — el estándar de OpenMapTiles. A partir de z14 el cliente hace overzoom: MapLibre escala la geometría del z14, que al ser vectorial se mantiene nítida (no se pixela). Puedes navegar perfectamente hasta z18+; la geometría de calle proviene del z14, así que el detalle posicional es el de z14, no falta de datos. Configura maxzoom alto en tu mapa (ej. 18-20) — el source declara maxzoom: 14 precisamente para habilitar el overzoom; subirlo en el source haría que MapLibre pida tiles z15+ inexistentes y el mapa quedaría en blanco arriba de z14.
Íconos de POI (sprite)
El sprite (sprite/sprite.json en el style.json) incluye ~60 íconos del set OpenMapTiles/Maki: comercios, gastronomía, salud, transporte, educación, ocio, etc. Las capas de POI usan icon-optional: true, así que un POI sin ícono mapeado igual muestra su etiqueta de texto — nunca desaparece. Si construyes tu propio estilo, los nombres de ícono coinciden con la clase OMT (restaurant, fuel, hospital, bank, pharmacy, bus, park, school…).
Proxear las fonts (glyphs)
Por defecto el style.json sirve los glyphs key-gated desde api.eviav.com/v1/fonts/{fontstack}/{range}.pbf con tu API key (cerrado, modelo Mapbox). Si tu app debe servir las tipografías desde tu propio dominio/proxy, pide el style.json con ?glyphs=<url> (debe ser https e incluir los placeholders {fontstack} y {range}, ej. https://tu-cdn.com/fonts/{fontstack}/{range}.pbf).
Versionado y estabilidad
La API es versionada en la URL (/v1/). Además, cada respuesta incluye el header X-Eviav-API-Version (ej. 2026.06.02) que identifica la revisión del contrato. Usalo para detectar cambios y para reportar incidencias.
Política de cambios
- Cambios aditivos y no-breaking (campos nuevos, endpoints nuevos, íconos nuevos) pueden salir en cualquier momento dentro de /v1. Tu integración no debe romperse por campos adicionales.
- Cambios breaking en el shape de una respuesta se publican como una nueva versión mayor en la URL (/v2/). Las dos versiones (/v1 y /v2) conviven un mínimo de 90 días.
- Las deprecaciones se anuncian en el changelog (eviav.com/changelog), por email a los owners de la organización, y vía el header Deprecation/Sunset en las respuestas afectadas.
Estilos de mapa (style.json)
El style.json incluye metadata.eviav:version y metadata.eviav:revision — versión semántica del estilo (colores, layers, fontstacks), independiente del version: 8 del spec MapLibre. Monitoreá ese valor para detectar cambios visuales antes de que afecten tu app.
Importante: apunta tu mapa a la URL hospedada del estilo (style: "streets" en el SDK, o la URL de tu estilo de Studio) — NO copies el style.json a tu app. Así recibes automáticamente las mejoras de cartografía, íconos y datos sin recompilar. Los tiles, estilos, glyphs y sprites se consumen como servicio con tu API key (modelo Mapbox/Google): el tileset crudo no es descargable y su redistribución no está permitida (ver Términos).
Discovery y health
GET api.eviav.com/health— liveness del gateway (200 OK).GET api.eviav.com/health/deep— estado por upstream (200/207/503).GET api.eviav.com/v1/tiles— lista estilos y tilesets disponibles.GET tiles.eviav.com/health,/styles,/version— health y discovery del CDN de tiles (para monitoring externo).
Atribución (requerida)
Eviav usa datos de OpenStreetMap bajo la licencia ODbL, que exige mostrar la atribución de forma visible. Es responsabilidad de quien integra mostrarla correctamente.
- Texto requerido:
© Eviav · © OpenMapTiles © OpenStreetMap contributors - Dónde: visible en todo momento sobre el mapa — esquina inferior, o un botón “i” que la despliegue. No es válido ocultarla detrás de varios taps ni omitirla.
- El style.json ya trae el texto en sources[].attribution y en metadata.eviav:attribution-placement. MapLibre lo muestra automáticamente con el control de atribución activado.
Color de fondo del estilo
El background del estilo es streets #CFE0E6 (azul agua) y dark #1B1813 (tinta cálida). Si usas un placeholder de carga, elige un color distinto a estos para distinguir “mapa cargando” de “layers fallaron”.
Soporte
Reportes de bugs y feature requests: [email protected].
Status del servicio: status.eviav.com.
Comercial / volúmenes altos: [email protected].
SLA, seguridad y legal
Antes de depender de Eviav en producción, revisa nuestros compromisos por escrito:
- SLA — 99.9% de uptime mensual, créditos de servicio por incumplimiento y tiempos de respuesta por severidad (P0–P3).
- Centro de confianza — estado de compliance, subprocesadores y status operacional.
- Seguridad, Privacidad y Términos.
- ¿Necesitas DPA o MSA firmados, o un SLA enterprise (99.99%) como adenda? Escríbenos a [email protected].