# Cómo obtener la posición GPS (latitud/longitud)

> Leer coordenadas del GPS del dispositivo con Location + retrieveGPSData(), o rastrear continuamente con GPSThread.

Source: https://crossfly.dev/es/docs/cookbook/device/obter-gps — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Cómo obtener la posición GPS (latitud/longitud)

**Propósito.** Obtener la **latitud/longitud** del dispositivo. Para una lectura puntual usa `Location`
+ `retrieveGPSData()`; para **rastreo continuo** usa `GPSThread`, que ya gestiona el thread.

## APIs usadas
- `new Location() throws IOException` — abre el GPS (precisión alta por defecto). También existe `new Location(int precision)` con `Location.HIGH_GPS_PRECISION` (solo GPS) o `Location.LOW_GPS_PRECISION` (GPS + Google Play Services / radio / wifi).
- `boolean retrieveGPSData()` — actualiza los campos estáticos; devuelve `true` si obtuvo un dato válido, `false` con señal baja (motivo en `Location.lowSignalReason`).
- `Location.getLatitude()` / `getLongitude()` (estáticos) — coordenadas; valen `Location.INVALID` sin señal.
- `Location.velocity` (**en nudos/knots**), `Location.pdop` (dilución de precisión), `Location.satellites`, `Location.direction` — campos estáticos.
- `void stop()` — **obligatorio**: cierra la API nativa y libera la instancia única. Solo puede haber **una** `Location` abierta a la vez (el constructor lanza `IOException` si ya hay una).
- `GPSThread(boolean highPrecision, int intervalInMS, Runnable onLocation, RunnableWith<Exception> onError)` — thread daemon que **arranca solo** en el constructor; `stopGps()` lo detiene y espera a que termine.

> **Cuidados.** Los campos de coordenada son **estáticos** — solo una lectura activa a la vez. Valida
> siempre contra `Location.INVALID` y verifica el `pdop` antes de confiar en la posición (LOW puede devolver
> 30m–1500m+). Llama a `stop()` al terminar (el `GPSThread` lo hace por ti en cada ciclo).

## Ejemplo completo (copiar, pegar y ejecutar)

```java
package com.exemplo;

import dev.crossfly.ui.*;                   // Window, Component, Button, Label
import dev.crossfly.ui.layout.*;            // VerticalLayout
import dev.crossfly.device.*;               // Location, Device

/** Pantalla principal ejecutable. Apunta el launcher (classname) y corre: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Label resultado = new Label("");
      add(LAY_AUTO, resultado);

      Button acao = new Button("Obter GPS");
      acao.onAction(c ->
      {
         // Lectura puntual (fuera de la paint thread) — siempre en try/finally p/ garantizar stop()
         new Thread(() ->
         {
            Location loc = null;
            try
            {
               loc = new Location();                              // abre el GPS (solo 1 a la vez)
               for (int i = 0; i < 120 && !loc.retrieveGPSData(); i++)
                  Device.sleep(500);                              // espera hasta ~60s por un fix
               double lat = Location.getLatitude();
               double lon = Location.getLongitude();
               if (lat != Location.INVALID && lon != Location.INVALID)
                  Component.onUI(() -> resultado.setText("Lat " + lat + " / Lon " + lon));
               else
                  Component.onUI(() -> resultado.setText("Sem sinal de GPS"));
            }
            catch (java.io.IOException e)
            {
               // ya había una Location abierta, o falló al abrir el GPS
               Component.onUI(() -> resultado.setText("Erro: " + e.getMessage()));
            }
            finally
            {
               if (loc != null) loc.stop();                       // OBLIGATORIO
            }
         }).start();
      });
      add(LAY_AUTO, acao);
   }
}
```

> El fragmento de abajo muestra **solo el núcleo**. Para ejecutarlo, colócalo en el esqueleto `MainWindow` de arriba.

## Fragmento mínimo

```java
// verificado en la fuente: Location.java (Location(), retrieveGPSData, getLatitude/Longitude, INVALID, stop); GPSThread.java (constructor, stopGps)
import dev.crossfly.device.*;              // Location
import dev.crossfly.core.*;                // GPSThread
import dev.crossfly.ui.*;                  // Component (onUI)

// 1) Lectura puntual (fuera de la paint thread) — siempre en try/finally p/ garantizar stop()
new Thread(() -> {
   Location loc = null;
   try {
      loc = new Location();                              // abre el GPS (solo 1 a la vez)
      for (int i = 0; i < 120 && !loc.retrieveGPSData(); i++)
         Device.sleep(500);                              // espera hasta ~60s por un fix
      double lat = Location.getLatitude();
      double lon = Location.getLongitude();
      if (lat != Location.INVALID && lon != Location.INVALID)
         Component.onUI(() -> mostrarPosicao(lat, lon)); // retorno fuera de la paint thread
   } catch (java.io.IOException e) {
      // ya había una Location abierta, o falló al abrir el GPS
   } finally {
      if (loc != null) loc.stop();                       // OBLIGATORIO
   }
}).start();

// 2) Rastreo continuo — GPSThread inicia sola; callbacks fuera de la paint thread
GPSThread gps = new GPSThread(true, 5000,                // alta precisión, cada 5s
   () -> {
      double lat = Location.getLatitude(), lon = Location.getLongitude();
      Component.onUI(() -> atualizarMapa(lat, lon));
   },
   err -> Component.onUI(() -> mostrarErro(err.getMessage())));
// ... cuando termine:
gps.stopGps();
```

## Profundiza
- Guía de ubicación: [Location & GPS](/docs/data-and-services/device-location-gps)
- Receta hermana: [escolher-local-no-mapa.md](/docs/cookbook/device/escolher-local-no-mapa)
