location
定位、指南针和地理编码。
定位与地理编码:申请权限、获取坐标、指南针方向,以及地址正反解析。
边界:本模块提供 CoreLocation 与 CLGeocoder,不包含地图 UI。要在 AppUI 里画地图标记,请看 定位地图 Cookbook。
#模块概览
| 项 | 说明 |
|---|---|
| 导入 | import location |
| 适合做什么 | 当前坐标、指南针、坐标转地址、地址转坐标 |
| 调用时机 | 定位放在按钮回调;reverse_geocode / geocode 走后台线程 |
| 推荐顺序 | 查/申请权限 → start_updates → get_location → stop_updates |
| 用完即停 | 不需要持续定位时调用 stop_updates();指南针同理用 stop_heading() |
#快速开始
下面脚本申请权限,读取一次当前坐标,并做一次逆地理编码:
已复制
import asyncio
import time
import location
if not location.is_authorized():
location.request_access()
time.sleep(0.5)
if not location.is_authorized():
print("位置权限未授权:", location.authorization_status())
else:
location.start_updates()
try:
time.sleep(1)
loc = location.get_location()
if loc:
lat, lon = loc["latitude"], loc["longitude"]
print(f"坐标: {lat:.5f}, {lon:.5f}")
print(f"精度: ±{loc['accuracy']:.0f} m")
place = asyncio.run(
asyncio.to_thread(location.reverse_geocode, lat, lon)
)
if place.get("success"):
print("地址:", place.get("name") or place.get("city"))
else:
print("地址解析失败:", place.get("error"))
else:
print("暂未获得坐标,请稍后重试")
finally:
location.stop_updates()
#AppUI 示例
把定位、指南针、地址解析都放进按钮回调;界面只展示当前状态。地理编码在后台线程执行,避免主线程死锁。
已复制
import threading
import time
import appui
import location
DEFAULT_LAT = 31.2304
DEFAULT_LON = 121.4737
state = appui.State(
auth="未查询",
status="点击按钮开始",
latitude=DEFAULT_LAT,
longitude=DEFAULT_LON,
accuracy="—",
heading="—",
place="—",
)
def _format_place(result):
if not result.get("success"):
return result.get("error", "解析失败")
parts = [
result.get("name"),
result.get("street"),
result.get("city"),
result.get("state"),
]
text = " · ".join(part for part in parts if part)
return text or str(result)
def refresh_location():
auth = location.authorization_status()
state.auth = auth
if auth in ("denied", "restricted"):
state.status = "定位权限不可用,请到系统设置允许定位"
return
if not location.is_authorized():
state.status = "正在请求定位权限..."
location.request_access()
state.auth = location.authorization_status()
if not location.is_authorized():
state.status = "请在系统弹窗中允许定位后,再次点击「刷新定位」"
return
state.status = "正在定位..."
location.start_updates()
try:
time.sleep(0.8)
loc = location.get_location()
if loc and loc.get("latitude") is not None:
state.latitude = float(loc["latitude"])
state.longitude = float(loc["longitude"])
state.accuracy = f"±{float(loc.get('accuracy', 0.0)):.0f} m"
state.status = "定位成功"
else:
state.status = "暂未获得坐标,请稍后再次点击「刷新定位」"
finally:
location.stop_updates()
def read_heading():
location.start_heading()
try:
time.sleep(0.5)
heading = location.get_heading()
if heading:
magnetic = float(heading.get("magnetic", 0.0))
state.heading = f"{magnetic:.0f}°"
state.status = "指南针已更新"
else:
state.heading = "—"
state.status = "暂未获得指南针数据"
finally:
location.stop_heading()
def reverse_lookup():
if state.status != "定位成功":
state.status = "请先点击「刷新定位」"
return
state.status = "正在解析地址..."
lat, lon = state.latitude, state.longitude
def work():
result = location.reverse_geocode(lat, lon)
state.place = _format_place(result)
state.status = "地址解析完成" if result.get("success") else "地址解析失败"
threading.Thread(target=work, daemon=True).start()
def body():
return appui.NavigationStack(
appui.Form([
appui.Section("操作", [
appui.Button("刷新定位", action=refresh_location)
.button_style("bordered_prominent"),
appui.Button("读取指南针", action=read_heading),
appui.Button("坐标转地址", action=reverse_lookup),
]),
appui.Section("定位", [
appui.LabeledContent("授权", value=state.auth),
appui.LabeledContent("纬度", value=f"{state.latitude:.5f}"),
appui.LabeledContent("经度", value=f"{state.longitude:.5f}"),
appui.LabeledContent("精度", value=state.accuracy),
appui.LabeledContent("指南针", value=state.heading),
]),
appui.Section("地址", [
appui.Text(state.place).foreground_color("secondaryLabel"),
appui.Text(state.status).font("caption").foreground_color("tertiaryLabel"),
]),
]).navigation_title("定位")
)
appui.run(body, state=state)
#API 参考
#速查
| API | 作用 |
|---|---|
authorization_status() | 查询授权 → 如 authorized_when_in_use |
is_authorized() | 是否已授权 → bool |
request_access() | 申请「使用期间」定位权限 |
start_updates() | 开始接收位置更新 |
stop_updates() | 停止位置更新 |
get_location() | 读取最新坐标 → dict 或 None |
start_heading() | 开始接收指南针更新 |
stop_heading() | 停止指南针更新 |
get_heading() | 读取指南针 → dict 或 None |
reverse_geocode(lat, lon) | 坐标转地址 → dict |
geocode(address) | 地址转坐标 → dict |
#权限
| API | 说明 |
|---|---|
authorization_status() | 返回状态字符串,不弹窗 |
is_authorized() | authorized_when_in_use 或 authorized_always 时为 True |
request_access() | 弹出系统授权,申请「使用期间」定位 |
authorization_status() 常见值:
not_determined— 尚未选择authorized_when_in_use— 使用期间允许authorized_always— 始终允许denied/restricted— 不可用
已复制
if not location.is_authorized():
location.request_access()
print(location.authorization_status())
也可用 permission 统一查询:permission.status("location")。
#定位
start_updates() — 开始接收 GPS 更新。第一次读取前通常需要等待一小段时间。
get_location() — 返回最新坐标字典,暂无数据时返回 None。
已复制
import time
import location
location.start_updates()
try:
time.sleep(1)
loc = location.get_location()
if loc:
print(loc["latitude"], loc["longitude"])
finally:
location.stop_updates()
| 字段 | 说明 |
|---|---|
latitude / longitude | 纬度 / 经度 |
altitude | 海拔(米) |
accuracy | 水平精度(米) |
timestamp | Unix 时间戳 |
stop_updates() — 停止定位,省电。
#指南针
| API | 说明 |
|---|---|
start_heading() | 开始接收指南针更新 |
get_heading() | 读取磁北、真北与精度 |
stop_heading() | 停止指南针更新 |
已复制
import time
import location
location.start_heading()
try:
time.sleep(0.5)
heading = location.get_heading()
if heading:
print(heading["magnetic"], heading["true_heading"])
finally:
location.stop_heading()
get_heading() 字段:magnetic、true_heading、accuracy。
注意:指南针与 GPS 是两条独立更新流,分别启动和停止。
#地理编码
reverse_geocode(latitude, longitude) — 坐标转地址。
geocode(address) — 地址转坐标。
已复制
import asyncio
import location
async def lookup():
place = await asyncio.to_thread(location.reverse_geocode, 31.2304, 121.4737)
coords = await asyncio.to_thread(location.geocode, "上海市黄浦区")
print(place)
print(coords)
asyncio.run(lookup())
成功时 reverse_geocode 通常带 success: True,并可能有 name、street、city、state、country、postal_code 等字段。geocode 成功时带 latitude、longitude。
注意:不要在 App 主线程同步调用地理编码;否则会返回code: -100错误。MiniApp / AppUI 回调里用asyncio.to_thread(...)或后台线程。
#AppUI MapView {#map-view}
在 AppUI 中用 appui.MapView 展示中心点与标记;先通过本模块拿到坐标,再在地图组件里绑定。完整示例见 定位地图 Cookbook。
#常见错误
| 错误写法 | 后果 | 修正 |
|---|---|---|
主线程直接 geocode / reverse_geocode | 返回 code: -100,可能死锁 | 用 asyncio.to_thread 或后台线程 |
未 start_updates 就读 get_location | 返回 None | 先启动更新并等待 0.5–1 秒 |
权限被拒后反复 request_access | 用户烦躁、仍拿不到坐标 | 提示去系统设置,不要循环弹窗 |
用完不 stop_updates | 持续耗电 | 读取完成后调用 stop_updates() |
#相关文档
| 文档 | 用途 |
|---|---|
| permission | 统一查询 location 权限 |
| weather | 按坐标或当前位置查天气 |
| 定位地图 Cookbook | AppUI MapView 标记当前位置 |
| 原生能力入口 | MiniApp 场景配方 |
#预期效果
运行示例后,界面应出现文档描述的目标结果;若与预期不符,先看「失败路径」并按返回值或日志排查。