# 如何让用户在地图上选择一个位置

> 打开一个带可拖动图钉和 GPS 的地图，让用户通过 LocationPicker 确认一个坐标（LatLon）。

Source: https://crossfly.dev/zh/docs/cookbook/device/escolher-local-no-mapa — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 如何让用户在地图上选择一个位置

**目的。** 打开一个带 **可拖动图钉** 和 GPS 跟踪的（Google）地图界面，让
用户确认一个位置。结果是一个通过回调交付的 `LatLon`。

## 使用的 API
- `LocationPicker(RunnableWith<LatLon> onCapture, double minPdop, boolean canDragPin, boolean highPrecision, boolean streetview)` — 完整构造函数（这是源码中真实的签名）。
  - `onCapture` — 使用已确认的 `LatLon` 调用的回调（可以为 `null`）。
  - `minPdop` — 可接受的最小 PDOP；确认按钮会一直隐藏，直到定位达到该精度（在桌面端始终可用）。
  - `canDragPin` — `true` 允许将图钉拖到自定义位置。
  - `highPrecision` — `true` 表示高精度 GPS。
  - `streetview` — `true` 表示在地图中启用街景。
- `LatLon getAnswer()` — 已确认的位置，若取消则为 `null`。
- 可配置的静态字段：`LocationPicker.defaultZoom`、`Instrucoes`、`GpsLocation`、`CustomLocation`、`Precision`、`Meters`。

> **与请求描述的差异。** `LocationPicker` **不** 使用 `show(onAnswer)` 来返回
> 结果：坐标通过在 **构造函数** 中传入的 `RunnableWith<LatLon> onCapture` 到达
> （同时也保存在 `getAnswer()` 中）。`onCapture` 只有在用户以非空位置 **确认** 时才会被调用。
> picker 内部运行它 **自己的** `GPSThread`（内部类），并且已经
> 通过 `Component.onUI(...)` 派发更新，因此你的回调在 paint 线程上执行。

## 完整示例（复制、粘贴并运行）

```java
package com.exemplo;

import dev.crossfly.ui.*;                   // Window, Button, Label
import dev.crossfly.ui.layout.*;            // VerticalLayout
import dev.crossfly.ui.picker.*;            // LocationPicker
import dev.crossfly.map.*;                  // LatLon
import dev.crossfly.util.*;                 // RunnableWith

/** 可执行的主界面。将 launcher 指向此类（classname）并运行：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("Escolher no mapa");
      acao.onAction(c ->
      {
         RunnableWith<LatLon> aoConfirmar = coord ->
         {
            // 已在 paint thread 上（picker 内部使用 Component.onUI）
            resultado.setText("Lat " + coord.lat + " / Lon " + coord.lon);
         };

         LocationPicker picker = new LocationPicker(
            aoConfirmar,   // 回调携带已确认的 LatLon
            30.0,          // minPdop：精度 <= 30m 时才允许确认
            true,          // canDragPin：用户可以拖动图钉
            true,          // highPrecision
            false);        // streetview
         picker.show();
      });
      add(LAY_AUTO, acao);
   }
}
```

> 下面的片段只展示 **核心部分**。要运行它，请把它放进上面的 `MainWindow` 骨架里。

## 最小片段

```java
// 已在源码验证：LocationPicker.java（构造函数第 129-141 行，onCapture/closeWith，getAnswer）
import dev.crossfly.ui.picker.*;           // LocationPicker
import dev.crossfly.map.*;                 // LatLon
import dev.crossfly.util.*;                // RunnableWith

RunnableWith<LatLon> aoConfirmar = coord -> {
   // 已在 paint thread 上（picker 内部使用 Component.onUI）
   double lat = coord.lat, lon = coord.lon;
   salvarLocal(lat, lon);
};

LocationPicker picker = new LocationPicker(
   aoConfirmar,   // 回调携带已确认的 LatLon
   30.0,          // minPdop：精度 <= 30m 时才允许确认
   true,          // canDragPin：用户可以拖动图钉
   true,          // highPrecision
   false);        // streetview
picker.show();

// 替代方案：之后通过 getAnswer() 读取结果（取消则为 null）
```

## 深入了解
- 定位指南：[Location & GPS](/docs/data-and-services/device-location-gps) · 组件：[LocationPicker](/docs/ui/widgets/locationpicker)
- 姊妹篇：[obter-gps.md](/docs/cookbook/device/obter-gps)
