PythonIDE Docs
中文
简体中文

location

定位、指南针和地理编码。

定位与地理编码:申请权限、获取坐标、指南针方向,以及地址正反解析。

边界:本模块提供 CoreLocation 与 CLGeocoder,不包含地图 UI。要在 AppUI 里画地图标记,请看 定位地图 Cookbook

#模块概览

说明
导入import location
适合做什么当前坐标、指南针、坐标转地址、地址转坐标
调用时机定位放在按钮回调;reverse_geocode / geocode 走后台线程
推荐顺序查/申请权限 → start_updatesget_locationstop_updates
用完即停不需要持续定位时调用 stop_updates();指南针同理用 stop_heading()

#快速开始

下面脚本申请权限,读取一次当前坐标,并做一次逆地理编码:

python
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 示例

把定位、指南针、地址解析都放进按钮回调;界面只展示当前状态。地理编码在后台线程执行,避免主线程死锁。

python
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()读取最新坐标 → dictNone
start_heading()开始接收指南针更新
stop_heading()停止指南针更新
get_heading()读取指南针 → dictNone
reverse_geocode(lat, lon)坐标转地址 → dict
geocode(address)地址转坐标 → dict

#权限

API说明
authorization_status()返回状态字符串,不弹窗
is_authorized()authorized_when_in_useauthorized_always 时为 True
request_access()弹出系统授权,申请「使用期间」定位

authorization_status() 常见值:

  • not_determined — 尚未选择
  • authorized_when_in_use — 使用期间允许
  • authorized_always — 始终允许
  • denied / restricted — 不可用
python
if not location.is_authorized():
    location.request_access()
print(location.authorization_status())

也可用 permission 统一查询:permission.status("location")

#定位

start_updates() — 开始接收 GPS 更新。第一次读取前通常需要等待一小段时间。

get_location() — 返回最新坐标字典,暂无数据时返回 None

python
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水平精度(米)
timestampUnix 时间戳

stop_updates() — 停止定位,省电。

#指南针

API说明
start_heading()开始接收指南针更新
get_heading()读取磁北、真北与精度
stop_heading()停止指南针更新
python
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() 字段:magnetictrue_headingaccuracy

注意:指南针与 GPS 是两条独立更新流,分别启动和停止。

#地理编码

reverse_geocode(latitude, longitude) — 坐标转地址。

geocode(address) — 地址转坐标。

python
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,并可能有 namestreetcitystatecountrypostal_code 等字段。geocode 成功时带 latitudelongitude

注意:不要在 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按坐标或当前位置查天气
定位地图 CookbookAppUI MapView 标记当前位置
原生能力入口MiniApp 场景配方

#预期效果

运行示例后,界面应出现文档描述的目标结果;若与预期不符,先看「失败路径」并按返回值或日志排查。