Read the original article:Continuous Location Tracking with Geofence Alerts
Continuous Location Tracking with Geofence Alerts
Requirement Description
Wearable apps often need continuous location tracking for fitness and navigation. Beyond just fetching the user's coordinates once, applications must also montior entry and exit from defined geographic areas (geofences).
This scenario shows how to:
Background Knowledge
Implementation Steps
1.Request user authorization using a UIAbility;
//EntryAbility.ets
// Use UIExtensionAbility: Replace import { UIAbility } from '@kit.AbilityKit' with import { UIExtensionAbility } from '@kit.AbilityKit'.
import { abilityAccessCtrl, common, Permissions, UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
const permissions: ArrayPermissions> = ['ohos.permission.LOCATION','ohos.permission.APPROX IMATELY_LOCATION'];
// Use UIExtensionAbility: Replace common.UIAbilityContext with common.UIExtensionContext.
function reqPermissionsFromUser(permissions: ArrayPermissions>, context: common.UIAbilityContext): void {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
// The return value of requestPermissionsFromUser determines whether to display a dialog box to request user authorization.
atManager.requestPermissionsFromUser(context, permissions).then((data) => {
let grantStatus: Arraynumber> = data.authResults;
let length: number = grantStatus.length;
for (let i = 0; i length; i++) {
if (grantStatus[i] === 0) {
// If the user grants the permission, the application continues the operation.
} else {
// If the user denies the permission, a message is displayed, indicating that user authorization is required and instructing the user to set the permission in Settings.
return;
}
}
// Authorization is successful.
}).catch((err: BusinessError) => {
console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
})
}
// Use UIExtensionAbility: Replace UIAbility with UIExtensionAbility.
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage): void {
// ...
windowStage.loadContent('pages/Index', (err, data) => {
reqPermissionsFromUser(permissions, this.context);
// ...
});
}
// ...
}
2.Applying for Permissions: ohos.permission.APPROXIMATELY_LOCATION, ohos.permission.APPROXIMATELY_LOCATION and ohos.permission.LOCATION.
//module.json5
"requestPermissions": [
{
"name": "ohos.permission.LOCATION",
"reason": "$string:location_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:location_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
],
3.Create a location request with interval.
4.Implement Haversine distance calculation to simulate geofence detection.
5.Display updates in UI and stop listener in onDisappear.
Code Snippet / Configuration
//MainPage.ets
import geoLocationManager from '@ohos.geoLocationManager';
import {BusinessError} from '@ohos.base'
interface GeoFenceType {
lat: number;
lon: number;
radius: number;
}
//Example geofence
const GEOFENCE: GeoFenceType = {lat: 22.87707, lon: 113.88187, radius: 100}
@Entry
@Component
struct MainPage {
@State latitude: string = "-"
@State longitude: string = "-"
@State insideFence: boolean = false
async aboutToAppear() {
//Use ContinuousLocationRequest as the input parameter.
let request:geoLocationManager.ContinuousLocationReque st = {'interval': 1, 'locationScenario': geoLocationManager.UserActivityScenario.NAVIGATION };
let locationCallback = (location:geoLocationManager.Location):void => {
console.info('locationCallback: data: ' + JSON.stringify(location));
this.latitude = location.latitude.toFixed(5)
this.longitude = location.longitude.toFixed(5)
const inside = this.isInsideGeofence(location.latitude, location.longitude)
this.insideFence = inside
}
try {
geoLocationManager.on('locationChange', request, locationCallback);
}catch (err){
let e: BusinessError = err as BusinessError
console.error(e.code + e.message)
}
}
aboutToDisappear() {
let locationChange = (location:geoLocationManager.Location):void => {
console.info('locationChange: data: ' + JSON.stringify(location));
};
geoLocationManager.off('locationChange',locationCh ange)
}
private isInsideGeofence(lat: number, lon: number) : boolean{
const R = 6371000;//Earth radius in meters
const dLat = (lat - GEOFENCE.lat) * Math.PI / 180
const dLon = (lon - GEOFENCE.lon) * Math.PI / 180
const a = Math.sin(dLat/2)**2 +
Math.cos(GEOFENCE.lat * Math.PI/180) * Math.cos(lat * Math.PI/180) *
Math.sin(dLon/2)**2
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
const distance = R * c
return distance GEOFENCE.radius
}
build() {
Column({space:6}){
Text(`Latitude: ${this.latitude}`).fontSize(12)
Text(`Longitude: ${this.longitude}`).fontSize(12)
Text(`Inside Geofence: ${this.insideFence ? 'YES' : 'NO'}`).fontSize(12)
} .justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
}
}
Test Results

Limitations or Considerations
Not applicable to apps targeting API Level
Related Documents or Links
Written by Emrecan Karakas
More...
Continuous Location Tracking with Geofence Alerts
Requirement Description
Wearable apps often need continuous location tracking for fitness and navigation. Beyond just fetching the user's coordinates once, applications must also montior entry and exit from defined geographic areas (geofences).
This scenario shows how to:
- Request user permission for location services.
- Start continuous location updates.
- Define a geofence (circle area)
- Trigger custom logic when entering/exiting the area.
- Stop updates when no longer needed.
Background Knowledge
- Location data is accessed via @ohos.geoLocationManager.
- Needs runtime permission handling.
- Updates may consume significant battery. Only subscribe when required.
- Geofencing is not directly built-in, must be implemented with math.
Implementation Steps
1.Request user authorization using a UIAbility;
//EntryAbility.ets
// Use UIExtensionAbility: Replace import { UIAbility } from '@kit.AbilityKit' with import { UIExtensionAbility } from '@kit.AbilityKit'.
import { abilityAccessCtrl, common, Permissions, UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
const permissions: ArrayPermissions> = ['ohos.permission.LOCATION','ohos.permission.APPROX IMATELY_LOCATION'];
// Use UIExtensionAbility: Replace common.UIAbilityContext with common.UIExtensionContext.
function reqPermissionsFromUser(permissions: ArrayPermissions>, context: common.UIAbilityContext): void {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
// The return value of requestPermissionsFromUser determines whether to display a dialog box to request user authorization.
atManager.requestPermissionsFromUser(context, permissions).then((data) => {
let grantStatus: Arraynumber> = data.authResults;
let length: number = grantStatus.length;
for (let i = 0; i length; i++) {
if (grantStatus[i] === 0) {
// If the user grants the permission, the application continues the operation.
} else {
// If the user denies the permission, a message is displayed, indicating that user authorization is required and instructing the user to set the permission in Settings.
return;
}
}
// Authorization is successful.
}).catch((err: BusinessError) => {
console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
})
}
// Use UIExtensionAbility: Replace UIAbility with UIExtensionAbility.
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage): void {
// ...
windowStage.loadContent('pages/Index', (err, data) => {
reqPermissionsFromUser(permissions, this.context);
// ...
});
}
// ...
}
2.Applying for Permissions: ohos.permission.APPROXIMATELY_LOCATION, ohos.permission.APPROXIMATELY_LOCATION and ohos.permission.LOCATION.
//module.json5
"requestPermissions": [
{
"name": "ohos.permission.LOCATION",
"reason": "$string:location_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:location_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
],
3.Create a location request with interval.
4.Implement Haversine distance calculation to simulate geofence detection.
5.Display updates in UI and stop listener in onDisappear.
Code Snippet / Configuration
//MainPage.ets
import geoLocationManager from '@ohos.geoLocationManager';
import {BusinessError} from '@ohos.base'
interface GeoFenceType {
lat: number;
lon: number;
radius: number;
}
//Example geofence
const GEOFENCE: GeoFenceType = {lat: 22.87707, lon: 113.88187, radius: 100}
@Entry
@Component
struct MainPage {
@State latitude: string = "-"
@State longitude: string = "-"
@State insideFence: boolean = false
async aboutToAppear() {
//Use ContinuousLocationRequest as the input parameter.
let request:geoLocationManager.ContinuousLocationReque st = {'interval': 1, 'locationScenario': geoLocationManager.UserActivityScenario.NAVIGATION };
let locationCallback = (location:geoLocationManager.Location):void => {
console.info('locationCallback: data: ' + JSON.stringify(location));
this.latitude = location.latitude.toFixed(5)
this.longitude = location.longitude.toFixed(5)
const inside = this.isInsideGeofence(location.latitude, location.longitude)
this.insideFence = inside
}
try {
geoLocationManager.on('locationChange', request, locationCallback);
}catch (err){
let e: BusinessError = err as BusinessError
console.error(e.code + e.message)
}
}
aboutToDisappear() {
let locationChange = (location:geoLocationManager.Location):void => {
console.info('locationChange: data: ' + JSON.stringify(location));
};
geoLocationManager.off('locationChange',locationCh ange)
}
private isInsideGeofence(lat: number, lon: number) : boolean{
const R = 6371000;//Earth radius in meters
const dLat = (lat - GEOFENCE.lat) * Math.PI / 180
const dLon = (lon - GEOFENCE.lon) * Math.PI / 180
const a = Math.sin(dLat/2)**2 +
Math.cos(GEOFENCE.lat * Math.PI/180) * Math.cos(lat * Math.PI/180) *
Math.sin(dLon/2)**2
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
const distance = R * c
return distance GEOFENCE.radius
}
build() {
Column({space:6}){
Text(`Latitude: ${this.latitude}`).fontSize(12)
Text(`Longitude: ${this.longitude}`).fontSize(12)
Text(`Inside Geofence: ${this.insideFence ? 'YES' : 'NO'}`).fontSize(12)
} .justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
}
}
Test Results

Limitations or Considerations
Not applicable to apps targeting API Level
Related Documents or Links
Written by Emrecan Karakas
More...