第七篇:地理编码与逆地理编码

本篇教程将学习地理编码(地址转坐标)和逆地理编码(坐标转地址)功能。

学习目标

  • 理解地理编码的概念
  • 实现地址到坐标的转换
  • 实现坐标到地址的转换
  • 结合地图点击获取地址信息

1. 核心概念

名称 说明 示例
地理编码 将结构化地址转换为经纬度坐标 “北京市朝阳区望京SOHO” → (39.99, 116.48)
逆地理编码 将经纬度坐标转换为结构化地址 (39.99, 116.48) → “北京市朝阳区望京街道…”

2. 核心类说明

类名 说明
GeocodeSearch 地理编码搜索类
GeocodeQuery 地理编码查询条件
GeocodeResult 地理编码结果
GeocodeAddress 地理编码地址信息
ReGeocodeQuery 逆地理编码查询条件
ReGeocodeResult 逆地理编码结果
ReGeocodeAddress 逆地理编码地址信息

3. 完整代码示例

创建文件 entry/src/main/ets/pages/Demo06_Geocode.ets

import {
  AMap,
  MapView,
  MapViewComponent,
  MapViewManager,
  MapViewCreateCallback,
  CameraUpdateFactory,
  LatLng,
  Marker,
  MarkerOptions,
  BitmapDescriptorFactory
} from '@amap/amap_lbs_map3d';
import {
  GeocodeSearch,
  GeocodeQuery,
  GeocodeResult,
  GeocodeAddress,
  ReGeocodeQuery,
  ReGeocodeResult,
  OnGeocodeSearchListener,
  AMapException,
  LatLonPoint
} from '@amap/amap_lbs_search';
import { inputMethod } from '@kit.IMEKit';

const MAP_VIEW_NAME = 'GeocodeDemo';

@Entry
@Component
struct Demo06_Geocode {
  private mapView: MapView | undefined = undefined;
  private aMap: AMap | undefined = undefined;
  private geocodeSearch: GeocodeSearch | undefined = undefined;
  private resultMarker: Marker | undefined = undefined;
  
  @State isMapReady: boolean = false;
  @State address: string = '北京市朝阳区望京SOHO';
  @State city: string = '北京';
  @State isSearching: boolean = false;
  @State resultInfo: string = '';
  @State clickedAddress: string = '点击地图获取地址';

  /**
   * 地理编码回调
   */
  private geocodeListener: OnGeocodeSearchListener = {
    // 地理编码回调(地址→坐标)
    onGeocodeSearched: (result: GeocodeResult | undefined, errorCode: number) => {
      this.isSearching = false;
      
      if (errorCode === AMapException.CODE_AMAP_SUCCESS) {
        if (result) {
          const addressList = result.getGeocodeAddressList();
          if (addressList && addressList.length > 0) {
            const address = addressList[0] as GeocodeAddress;
            const point = address.getLatLonPoint();
            
            if (point) {
              const lat = point.getLatitude();
              const lng = point.getLongitude();
              
              this.resultInfo = `地理编码结果:\n地址: ${address.getFormatAddress() || this.address}\n坐标: ${lat.toFixed(6)}, ${lng.toFixed(6)}\n级别: ${address.getLevel() || '未知'}`;
              
              // 移动地图并添加标记
              this.showResultOnMap(lat, lng, address.getFormatAddress() || this.address);
            }
          } else {
            this.resultInfo = '未找到该地址的坐标信息';
          }
        }
      } else {
        this.resultInfo = `地理编码失败: 错误码 ${errorCode}`;
        console.error('[Geocode] Search failed:', errorCode);
      }
    },
    
    // 逆地理编码回调(坐标→地址)
    onReGeocodeSearched: (result: ReGeocodeResult | undefined, errorCode: number) => {
      this.isSearching = false;
      
      if (errorCode === AMapException.CODE_AMAP_SUCCESS) {
        if (result) {
          const reGeocodeAddress = result.getReGeocodeAddress();
          
          if (reGeocodeAddress) {
            const formatAddress = reGeocodeAddress.getFormatAddress() || '';
            const province = reGeocodeAddress.getProvince() || '';
            const city = reGeocodeAddress.getCity() || '';
            const district = reGeocodeAddress.getDistrict() || '';
            const township = reGeocodeAddress.getTownship() || '';
            const neighborhood = reGeocodeAddress.getNeighborhood() || '';
            const building = reGeocodeAddress.getBuilding() || '';
            
            this.clickedAddress = formatAddress;
            
            this.resultInfo = `逆地理编码结果:\n完整地址: ${formatAddress}\n省份: ${province}\n城市: ${city}\n区县: ${district}\n街道: ${township}\n社区: ${neighborhood}\n建筑: ${building}`;
            
            // 获取POI信息
            const pois = reGeocodeAddress.getPois();
            if (pois && pois.length > 0) {
              const nearestPoi = pois[0];
              this.resultInfo += `\n\n最近POI: ${nearestPoi.getTitle()}\n距离: ${nearestPoi.getDistance()}`;
            }
          }
        }
      } else {
        this.resultInfo = `逆地理编码失败: 错误码 ${errorCode}`;
        console.error('[ReGeocode] Search failed:', errorCode);
      }
    }
  };

  private mapViewCreateCallback: MapViewCreateCallback = 
    (mapview: MapView | undefined, mapViewName: string | undefined) => {
      if (!mapview || mapViewName !== MAP_VIEW_NAME) return;

      this.mapView = mapview;
      this.mapView.onCreate();
      
      this.mapView.getMapAsync((map: AMap) => {
        this.aMap = map;
        this.isMapReady = true;
        
        // 移动到北京
        const beijing = new LatLng(39.909187, 116.397451);
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(beijing, 13));
        
        // 启用控件
        map.getUiSettings()?.setZoomControlsEnabled(true);
        
        // 地图点击事件 - 执行逆地理编码
        map.setOnMapClickListener((point: LatLng) => {
          console.info('[Geocode] Map clicked:', point.latitude, point.longitude);
          this.doReGeocode(point.latitude, point.longitude);
        });
        
        // 地图长按事件 - 添加标记
        map.setOnMapLongClickListener((point: LatLng) => {
          this.showResultOnMap(point.latitude, point.longitude, '长按标记');
        });
      });
    };

  /**
   * 执行地理编码(地址→坐标)
   */
  private doGeocode(): void {
    if (!this.geocodeSearch || !this.address.trim()) {
      this.resultInfo = '请输入地址';
      return;
    }
    
    inputMethod.getController().stopInputSession();
    
    this.isSearching = true;
    this.resultInfo = '正在查询...';
    
    // 创建地理编码查询
    const query = new GeocodeQuery(this.address, this.city);
    
    // 执行异步查询
    this.geocodeSearch.getFromLocationNameAsyn(query);
    
    console.info('[Geocode] Searching:', this.address, 'in', this.city);
  }

  /**
   * 执行逆地理编码(坐标→地址)
   */
  private doReGeocode(lat: number, lng: number): void {
    if (!this.geocodeSearch) return;
    
    this.isSearching = true;
    this.clickedAddress = '正在获取地址...';
    
    // 添加/更新标记
    this.showResultOnMap(lat, lng, '');
    
    // 创建逆地理编码查询
    const point = new LatLonPoint(lat, lng);
    const query = new ReGeocodeQuery(
      point,
      200,       // 搜索半径(米)
      'base'     // 返回类型:base-基础信息,all-详细信息
    );
    
    // 执行异步查询
    this.geocodeSearch.getFromLocationAsyn(query);
    
    console.info('[ReGeocode] Searching:', lat, lng);
  }

  /**
   * 在地图上显示结果
   */
  private showResultOnMap(lat: number, lng: number, title: string): void {
    if (!this.aMap) return;
    
    // 移除之前的标记
    if (this.resultMarker) {
      this.resultMarker.remove();
    }
    
    // 添加新标记
    const options = new MarkerOptions();
    options.setPosition(new LatLng(lat, lng));
    options.setTitle(title || '查询结果');
    options.setIcon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));
    options.setAnchor(0.5, 1.0);
    
    this.resultMarker = this.aMap.addMarker(options);
    
    // 移动地图
    this.aMap.animateCamera(
      CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lng), 16),
      500
    );
  }

  /**
   * 使用预设地址
   */
  private usePresetAddress(preset: string): void {
    this.address = preset;
    this.doGeocode();
  }

  aboutToAppear(): void {
    MapViewManager.getInstance()
      .registerMapViewCreatedCallback(this.mapViewCreateCallback);
    
    // 初始化地理编码搜索
    const context = getContext(this);
    this.geocodeSearch = new GeocodeSearch(context);
    this.geocodeSearch.setOnGeocodeSearchListener(this.geocodeListener);
  }

  aboutToDisappear(): void {
    if (this.resultMarker) {
      this.resultMarker.remove();
    }
    
    MapViewManager.getInstance()
      .unregisterMapViewCreatedCallback(this.mapViewCreateCallback);
    
    if (this.mapView) {
      this.mapView.onDestroy();
      this.mapView = undefined;
      this.aMap = undefined;
    }
  }

  build() {
    Column() {
      // 标题栏
      Row() {
        Text('地理编码')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor(Color.White)
      }
      .width('100%')
      .height(50)
      .padding({ left: 16 })
      .backgroundColor('#009688')

      // 搜索栏
      Column() {
        // 地址输入
        Row() {
          TextInput({ text: this.address, placeholder: '输入地址' })
            .layoutWeight(1)
            .height(40)
            .onChange((value: string) => { this.address = value; })
          
          TextInput({ text: this.city, placeholder: '城市' })
            .width(70)
            .height(40)
            .margin({ left: 8 })
            .onChange((value: string) => { this.city = value; })
          
          Button('查询')
            .height(40)
            .margin({ left: 8 })
            .enabled(!this.isSearching)
            .onClick(() => this.doGeocode())
        }
        .width('100%')

        // 预设地址快捷按钮
        Text('快捷地址:')
          .fontSize(12)
          .fontColor('#666')
          .width('100%')
          .margin({ top: 8 })
        
        Flex({ wrap: FlexWrap.Wrap }) {
          Button('天安门')
            .fontSize(11)
            .height(28)
            .margin({ right: 6, top: 4 })
            .backgroundColor('#e0e0e0')
            .fontColor('#333')
            .onClick(() => this.usePresetAddress('北京市天安门'))
          
          Button('东方明珠')
            .fontSize(11)
            .height(28)
            .margin({ right: 6, top: 4 })
            .backgroundColor('#e0e0e0')
            .fontColor('#333')
            .onClick(() => {
              this.city = '上海';
              this.usePresetAddress('上海市东方明珠');
            })
          
          Button('西湖')
            .fontSize(11)
            .height(28)
            .margin({ right: 6, top: 4 })
            .backgroundColor('#e0e0e0')
            .fontColor('#333')
            .onClick(() => {
              this.city = '杭州';
              this.usePresetAddress('杭州市西湖');
            })
        }
      }
      .padding(12)
      .backgroundColor('#f5f5f5')

      // 地图区域
      Stack() {
        MapViewComponent({ mapViewName: MAP_VIEW_NAME })
          .width('100%')
          .height('100%')
        
        // 点击提示
        Column() {
          Text('点击地图获取地址')
            .fontSize(11)
            .fontColor('#fff')
          Text(this.clickedAddress)
            .fontSize(10)
            .fontColor('#fff')
            .margin({ top: 4 })
            .maxLines(2)
        }
        .padding(8)
        .backgroundColor('rgba(0,0,0,0.7)')
        .borderRadius(8)
        .position({ x: 10, y: 10 })
      }
      .width('100%')
      .layoutWeight(1)

      // 结果显示区
      if (this.resultInfo) {
        Scroll() {
          Text(this.resultInfo)
            .fontSize(12)
            .fontColor('#333')
            .width('100%')
        }
        .height(120)
        .padding(12)
        .backgroundColor(Color.White)
      }
    }
    .width('100%')
    .height('100%')
  }
}

4. 地理编码详解

4.1 GeocodeQuery 参数

// 创建查询
const query = new GeocodeQuery(
  address,  // 地址字符串
  city      // 城市(可为空)
);

// 城市参数说明:
// - 可传入城市名称,如"北京"
// - 可传入城市编码,如"010"
// - 可传入adcode,如"110000"
// - 传空字符串则全国搜索

4.2 GeocodeAddress 结果

interface GeocodeAddress {
  getFormatAddress(): string;    // 格式化地址
  getLatLonPoint(): LatLonPoint; // 坐标
  getProvince(): string;         // 省份
  getCity(): string;             // 城市
  getDistrict(): string;         // 区县
  getAdcode(): string;           // 区域编码
  getLevel(): string;            // 匹配级别
  getTownship(): string;         // 街道/乡镇
}

5. 逆地理编码详解

5.1 ReGeocodeQuery 参数

const query = new ReGeocodeQuery(
  point,    // LatLonPoint 坐标点
  radius,   // 搜索半径(米),取值范围0-3000
  type      // 返回类型:"base"基础信息,"all"详细信息
);

5.2 ReGeocodeAddress 结果

interface ReGeocodeAddress {
  getFormatAddress(): string;   // 格式化地址
  getProvince(): string;        // 省
  getCity(): string;            // 市
  getDistrict(): string;        // 区/县
  getTownship(): string;        // 乡镇/街道
  getNeighborhood(): string;    // 社区
  getBuilding(): string;        // 建筑物
  getAdCode(): string;          // 区域编码
  getCityCode(): string;        // 城市编码
  getCountry(): string;         // 国家
  
  // 获取POI列表(type为"all"时有效)
  getPois(): PoiItem[];
  
  // 获取道路信息
  getRoads(): Road[];
  
  // 获取交叉口信息
  getCrossroads(): Crossroad[];
}

6. 同步与异步调用

6.1 异步调用(推荐)

// 地理编码
geocodeSearch.getFromLocationNameAsyn(query);

// 逆地理编码
geocodeSearch.getFromLocationAsyn(query);

// 结果通过OnGeocodeSearchListener回调返回

6.2 同步调用

// 地理编码(会阻塞线程)
const result = await geocodeSearch.getFromLocationName(query);

// 逆地理编码
const result = await geocodeSearch.getFromLocation(query);

7. 匹配级别说明

级别 说明
国家 匹配到国家级别
匹配到省级别
匹配到市级别
区县 匹配到区县级别
开发区 匹配到开发区级别
乡镇 匹配到乡镇级别
村庄 匹配到村庄级别
道路 匹配到道路级别
门牌号 匹配到门牌号级别
POI 匹配到兴趣点级别

8. 实用技巧

8.1 批量地理编码

async function batchGeocode(addresses: string[], city: string): Promise<LatLonPoint[]> {
  const results: LatLonPoint[] = [];
  
  for (const addr of addresses) {
    const query = new GeocodeQuery(addr, city);
    const result = await geocodeSearch.getFromLocationName(query);
    
    if (result) {
      const list = result.getGeocodeAddressList();
      if (list && list.length > 0) {
        const point = list[0].getLatLonPoint();
        if (point) {
          results.push(point);
        }
      }
    }
  }
  
  return results;
}

8.2 结合地图操作

// 地图点击时自动获取地址
aMap.setOnMapClickListener((point: LatLng) => {
  const latLonPoint = new LatLonPoint(point.latitude, point.longitude);
  const query = new ReGeocodeQuery(latLonPoint, 200, 'base');
  geocodeSearch.getFromLocationAsyn(query);
});

本篇小结

本篇教程我们学习了:

  • ✅ 地理编码的概念和使用场景
  • ✅ 地址转坐标的实现
  • ✅ 坐标转地址的实现
  • ✅ 结合地图点击获取地址
  • ✅ 解析返回的详细地址信息

下一篇我们将学习路线规划功能。

班级
https://developer.huawei.com/consumer/cn/training/classDetail/fd34ff9286174e848d34cde7f512ce22?type=1%3Fha_source%3Dhmosclass&ha_sourceId=89000248

源码地址
https://gitcode.com/daleishen/gaodehmjiaocheng.git

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐