Continuous Location Tracking with Geofence Alerts

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • MyrinNew
    Senior Member
    • Feb 2024
    • 5168

    #1

    Continuous Location Tracking with Geofence Alerts

    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:

    1. Request user permission for location services.
    2. Start continuous location updates.
    3. Define a geofence (circle area)
    4. Trigger custom logic when entering/exiting the area.
    5. 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...
Working...