Mobile Development 16 min read

HarmonyOS 7 Reverse Geocoding Empty Result? Fix GCJ02 to WGS84 Conversion

This article solves a HarmonyOS 7 issue where getAddressesFromLocation returns an empty array without errors, revealing the root cause is a coordinate system mismatch: Map Kit provides GCJ02 coordinates while the geocoding API requires WGS84, and provides a conversion algorithm and complete implementation.

51CTO HarmonyOS Developer Community
51CTO HarmonyOS Developer Community
51CTO HarmonyOS Developer Community
HarmonyOS 7 Reverse Geocoding Empty Result? Fix GCJ02 to WGS84 Conversion

Problem: Reverse Geocoding Returns Empty Array Without Errors

Developers using HarmonyOS 7 encounter a silent failure: calling geoLocationManager.getAddressesFromLocation after a map long-press returns an empty data array and err is undefined. The callback executes normally, permissions appear correct, yet no address is resolved.

Troubleshooting Checklist

Confirm location permissions are declared in module.json5 and requested at runtime ( ohos.permission.APPROXIMATELY_LOCATION and ohos.permission.LOCATION).

Verify geoLocationManager.isGeocoderAvailable() returns true.

Ensure device is online, location switch is on, and test on a real device (emulator often returns false).

Critical: If coordinates come from Map Kit, convert GCJ02 to WGS84 before passing to getAddressesFromLocation.

For domestic apps, set locale: 'zh' in the request.

Two Coordinate Systems on HarmonyOS

Map Kit callbacks (map click, marker) — GCJ02 — Chinese encrypted coordinate system, mandatory in mainland China.

geoLocationManager reverse geocoding API — WGS84 — GPS raw coordinates, international standard.

GCJ02 applies a non-linear offset of 300–500 meters over WGS84. Feeding GCJ02 coordinates into a WGS84-only service shifts the query location by hundreds of meters, causing the backend to find no matching address — hence the empty result despite a valid request.

Permission Setup

Declare in module.json5:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "reason": "$string:reason_for_location",
        "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
      },
      {
        "name": "ohos.permission.LOCATION",
        "reason": "$string:reason_for_location",
        "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
      }
    ]
  }
}

Request at runtime:

import { abilityAccessCtrl, common, Permissions } from '@kit.AbilityKit';

private async requestPermission(): Promise<void> {
  let perms: Array<Permissions> = ['ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION'];
  let context = getContext(this) as common.UIAbilityContext;
  let atManager = abilityAccessCtrl.createAtManager();
  await atManager.requestPermissionsFromUser(context, perms);
}

Service Availability Check

if (!geoLocationManager.isGeocoderAvailable()) {
  // Service unavailable: no network, emulator unsupported, location switch off
  return;
}

On emulators isGeocoderAvailable() typically returns false; real device with network and location enabled is required.

Core Solution: GCJ02 to WGS84 Conversion

The conversion algorithm computes the offset ( dLat, dLon) using the Chinese encryption parameters and subtracts it from the original coordinates. Coordinates outside mainland China (longitude < 72.004 or > 137.8347, latitude < 0.8293 or > 55.8271) are returned unchanged.

function gcj02ToWgs84(lat: number, lon: number): number[] {
  let a = 6378245.0;
  let ee = 0.00669342162296594323;
  // Overseas coordinates need no conversion
  if (lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271) {
    return [lat, lon];
  }
  let dLat = transformLat(lon - 105.0, lat - 35.0);
  let dLon = transformLon(lon - 105.0, lat - 35.0);
  let radLat = lat / 180.0 * Math.PI;
  let magic = Math.sin(radLat);
  magic = 1 - ee * magic * magic;
  let sqrtMagic = Math.sqrt(magic);
  dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * Math.PI);
  dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.PI);
  // Subtract offset to obtain WGS84
  return [lat - dLat, lon - dLon];
}

function transformLat(x: number, y: number): number {
  let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
  ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
  ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;
  ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
  return ret;
}

function transformLon(x: number, y: number): number {
  let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
  ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
  ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;
  ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;
  return ret;
}
transformLat

and transformLon implement the non-linear encryption simulation using trigonometric series.

Reverse Geocoding Call

private reverseGeocode(lat: number, lon: number): void {
  // Convert to WGS84 first
  let wgs84: number[] = gcj02ToWgs84(lat, lon);

  let request: geoLocationManager.ReverseGeoCodeRequest = {
    latitude: wgs84[0],
    longitude: wgs84[1],
    maxItems: 1,
    locale: 'zh'  // Essential for Chinese address results
  };

  geoLocationManager.getAddressesFromLocation(request).then((data: Array<geoLocationManager.GeoAddress>) => {
    if (data.length === 0) {
      // Still empty? Check network and location switch
      return;
    }
    let addr: geoLocationManager.GeoAddress = data[0];
    let province: string = addr.administrativeArea ?? '';
    let city: string = addr.locality ?? '';
    let district: string = addr.subLocality ?? '';
    this.bubbleText = `${province}${city}${district}`;
  }).catch((error: BusinessError) => {
    // Handle error
  });
}

Key parameters: locale: 'zh' — forces Chinese address output; omitting it may yield English or no result. maxItems: 1 — limits results to one, sufficient for a map bubble. GeoAddress fields: administrativeArea (province), locality (city), subLocality (district).

Mock Data for Testing

interface MockLocation {
  label: string;
  lat: number;
  lon: number;
}

const MOCK_LOCATIONS: MockLocation[] = [
  { label: '北京天安门', lat: 39.9098, lon: 116.4044 },
  { label: '上海东方明珠', lat: 31.2413, lon: 121.5064 },
  { label: '广州塔', lat: 23.1080, lon: 113.3311 },
  { label: '成都天府广场', lat: 30.6598, lon: 104.0633 },
  { label: '杭州西湖', lat: 30.2420, lon: 120.1485 },
];

These coordinates are GCJ02 (as returned by Map Kit). Clicking a list item triggers the conversion and reverse geocoding flow.

Complete Source Code

The full component includes imports, conversion functions, mock data, permission request, reverse geocoding method, and a UI with a selectable list and result bubble. The component uses @Entry, @Component, @State, ForEach, Row, Column, Text, and event handlers. (See the article for the complete listing.)

Conclusion

The silent empty-array symptom misleads developers into checking permissions first. The real culprit is the coordinate system mismatch: Map Kit delivers GCJ02, while getAddressesFromLocation expects WGS84. Always verify isGeocoderAvailable(), permissions, and network, then convert coordinates if they originate from Map Kit. Alternatively, use Map Kit's built-in site.reverseGeocode which handles the conversion internally.

Effect preview 1
Effect preview 1
Effect preview 2
Effect preview 2

}

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Mobile DevelopmentHarmonyOSreverse geocodingWGS84coordinate conversionGCJ02geoLocationManagerMap Kit
51CTO HarmonyOS Developer Community
Written by

51CTO HarmonyOS Developer Community

The HarmonyOS Developer Community is a learning-oriented community for developers to learn, communicate, ask questions, and share.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.