Skip to content

Commit bb812ed

Browse files
committed
feat: add support for heading
1 parent bb02a2d commit bb812ed

10 files changed

Lines changed: 138 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# [8.1.0](https://github.com/ionic-team/capacitor-geolocation/compare/v8.0.0...v8.1.0) (2026-02-02)
2+
3+
### Features
4+
5+
* **heading:** Added support for accurate heading information across all platforms.
6+
* **heading:** The `heading` property on iOS, Android, and Web now prioritizes actual compass bearing (true/magnetic heading) when available, falling back to direction of travel (course).
7+
* **coords:** Added `magneticHeading`, `trueHeading`, `headingAccuracy`, and `course` to `Position.coords`.
8+
* **web:** Improved heading support using `DeviceOrientation` APIs.
9+
110
# [8.0.0](https://github.com/ionic-team/capacitor-geolocation/compare/v7.1.6...v8.0.0) (2025-12-08)
211

312

CapacitorGeolocation.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ Pod::Spec.new do |s|
1313
s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
1414
s.ios.deployment_target = '15.0'
1515
s.dependency 'Capacitor'
16-
s.dependency 'IONGeolocationLib', spec='2.0.0'
16+
s.dependency 'IONGeolocationLib', '2.1.0'
1717
s.swift_version = '5.1'
1818
end

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,10 @@ Not available on web.
159159

160160
#### Position
161161

162-
| Prop | Type | Description | Since |
163-
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ----- |
164-
| **`timestamp`** | <code>number</code> | Creation timestamp for coords | 1.0.0 |
165-
| **`coords`** | <code>{ latitude: number; longitude: number; accuracy: number; altitudeAccuracy: number \| null; altitude: number \| null; speed: number \| null; heading: number \| null; }</code> | The GPS coordinates along with the accuracy of the data | 1.0.0 |
162+
| Prop | Type | Description | Since |
163+
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ----- |
164+
| **`timestamp`** | <code>number</code> | Creation timestamp for coords | 1.0.0 |
165+
| **`coords`** | <code>{ latitude: number; longitude: number; accuracy: number; altitudeAccuracy: number \| null; altitude: number \| null; speed: number \| null; heading: number \| null; magneticHeading: number \| null; trueHeading: number \| null; headingAccuracy: number \| null; course: number \| null; }</code> | The GPS coordinates along with the accuracy of the data | 1.0.0 |
166166

167167

168168
#### PositionOptions

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ dependencies {
8181
implementation project(':capacitor-android')
8282
}
8383

84-
implementation("io.ionic.libs:iongeolocation-android:2.1.0")
84+
implementation("io.ionic.libs:iongeolocation-android:2.2.0")
8585
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
8686

8787
implementation 'com.google.code.gson:gson:2.13.2'

android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,10 @@ class GeolocationPlugin : Plugin() {
255255
locationResult.altitudeAccuracy?.let { put("altitudeAccuracy", it) }
256256
put("speed", locationResult.speed)
257257
put("heading", locationResult.heading)
258+
locationResult.magneticHeading?.let { put("magneticHeading", it) }
259+
locationResult.trueHeading?.let { put("trueHeading", it) }
260+
locationResult.headingAccuracy?.let { put("headingAccuracy", it) }
261+
locationResult.course?.let { put("course", it) }
258262
}
259263
return JSObject().apply {
260264
put("timestamp", locationResult.timestamp)

ios/Sources/GeolocationPlugin/GeolocationConstants.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,9 @@ enum Constants {
3333
static let speed: String = "speed"
3434
static let timestamp: String = "timestamp"
3535
static let altitudeAccuracy: String = "altitudeAccuracy"
36+
static let magneticHeading: String = "magneticHeading"
37+
static let trueHeading: String = "trueHeading"
38+
static let headingAccuracy: String = "headingAccuracy"
39+
static let course: String = "course"
3640
}
3741
}

ios/Sources/GeolocationPlugin/IONGLOCPositionModel+JSONTransformer.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,14 @@ extension IONGLOCPositionModel {
1010
}
1111

1212
private var coordsJSObject: JSObject {
13-
[
13+
let headingValue = trueHeading != -1.0 ? trueHeading : (magneticHeading != -1.0 ? magneticHeading : course)
14+
return [
1415
Constants.Position.altitude: altitude,
15-
Constants.Position.heading: course,
16+
Constants.Position.heading: headingValue != -1.0 ? headingValue : NSNull(),
17+
Constants.Position.magneticHeading: magneticHeading != -1.0 ? magneticHeading : NSNull(),
18+
Constants.Position.trueHeading: trueHeading != -1.0 ? trueHeading : NSNull(),
19+
Constants.Position.headingAccuracy: headingAccuracy != -1.0 ? headingAccuracy : NSNull(),
20+
Constants.Position.course: course,
1621
Constants.Position.accuracy: horizontalAccuracy,
1722
Constants.Position.latitude: latitude,
1823
Constants.Position.longitude: longitude,

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@capacitor/geolocation",
3-
"version": "8.0.0",
3+
"version": "8.1.0",
44
"description": "The Geolocation API provides simple methods for getting and tracking the current position of the device using GPS, along with altitude, heading, and speed information if available.",
55
"main": "dist/plugin.cjs.js",
66
"module": "dist/esm/index.js",
@@ -88,4 +88,4 @@
8888
"src": "android"
8989
}
9090
}
91-
}
91+
}

src/definitions.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,43 @@ export interface Position {
140140
speed: number | null;
141141

142142
/**
143-
* The heading the user is facing (if available)
143+
* The heading the user is facing (if available).
144+
*
145+
* Historically, this field returned the direction of travel (course) on iOS and Android.
146+
* It now prioritizes the compass heading (true or magnetic) if available, falling back
147+
* to the direction of travel (course).
144148
*
145149
* @since 1.0.0
146150
*/
147151
heading: number | null;
152+
153+
/**
154+
* The heading (measured in degrees) relative to magnetic north.
155+
*
156+
* @since 8.1.0
157+
*/
158+
magneticHeading: number | null | undefined;
159+
160+
/**
161+
* The heading (measured in degrees) relative to true north.
162+
*
163+
* @since 8.1.0
164+
*/
165+
trueHeading: number | null | undefined;
166+
167+
/**
168+
* The maximum deviation (measured in degrees) between the reported heading and the true geomagnetic heading.
169+
*
170+
* @since 8.1.0
171+
*/
172+
headingAccuracy: number | null | undefined;
173+
174+
/**
175+
* The direction in which the device is travelling, measured in degrees and relative to due north.
176+
*
177+
* @since 8.1.0
178+
*/
179+
course: number | null | undefined;
148180
};
149181
}
150182

src/web.ts

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,80 @@ import type {
1010
} from './definitions';
1111

1212
export class GeolocationWeb extends WebPlugin implements GeolocationPlugin {
13+
private latestOrientation: {
14+
magneticHeading: number | null;
15+
trueHeading: number | null;
16+
headingAccuracy: number | null;
17+
} | null = null;
18+
19+
constructor() {
20+
super();
21+
if (typeof window !== 'undefined') {
22+
const win = window as any;
23+
if ('ondeviceorientationabsolute' in win) {
24+
win.addEventListener('deviceorientationabsolute', (event: any) => this.updateOrientation(event, true), true);
25+
} else if ('ondeviceorientation' in win) {
26+
win.addEventListener('deviceorientation', (event: any) => this.updateOrientation(event, false), true);
27+
}
28+
}
29+
}
30+
31+
private updateOrientation(event: any, isAbsolute: boolean) {
32+
let trueHeading: number | null = null;
33+
let magneticHeading: number | null = null;
34+
let headingAccuracy: number | null = null;
35+
36+
if (isAbsolute && event.alpha !== null) {
37+
trueHeading = (360 - event.alpha) % 360;
38+
} else if (event.webkitCompassHeading !== undefined && event.webkitCompassHeading !== null) {
39+
magneticHeading = event.webkitCompassHeading;
40+
headingAccuracy = event.webkitCompassAccuracy;
41+
} else if (event.alpha !== null && event.absolute === true) {
42+
trueHeading = (360 - event.alpha) % 360;
43+
} else if (event.alpha !== null) {
44+
magneticHeading = (360 - event.alpha) % 360;
45+
}
46+
47+
if (trueHeading !== null || magneticHeading !== null) {
48+
this.latestOrientation = {
49+
trueHeading,
50+
magneticHeading,
51+
headingAccuracy,
52+
};
53+
}
54+
}
55+
56+
private augmentPosition(pos: globalThis.GeolocationPosition): Position {
57+
const coords = pos.coords;
58+
const orientation = this.latestOrientation;
59+
60+
const heading = orientation?.trueHeading ?? orientation?.magneticHeading ?? coords.heading ?? null;
61+
62+
return {
63+
timestamp: pos.timestamp,
64+
coords: {
65+
latitude: coords.latitude,
66+
longitude: coords.longitude,
67+
accuracy: coords.accuracy,
68+
altitude: coords.altitude,
69+
altitudeAccuracy: (coords as any).altitudeAccuracy,
70+
speed: coords.speed,
71+
heading: heading,
72+
magneticHeading: orientation?.magneticHeading ?? null,
73+
trueHeading: orientation?.trueHeading ?? null,
74+
headingAccuracy: orientation?.headingAccuracy ?? null,
75+
course: coords.heading ?? null,
76+
},
77+
};
78+
}
79+
1380
async getCurrentPosition(options?: PositionOptions): Promise<Position> {
1481
return new Promise((resolve, reject) => {
1582
navigator.geolocation.getCurrentPosition(
16-
(pos) => {
17-
resolve(pos);
83+
pos => {
84+
resolve(this.augmentPosition(pos));
1885
},
19-
(err) => {
86+
err => {
2087
reject(err);
2188
},
2289
{
@@ -31,10 +98,10 @@ export class GeolocationWeb extends WebPlugin implements GeolocationPlugin {
3198

3299
async watchPosition(options: PositionOptions, callback: WatchPositionCallback): Promise<CallbackID> {
33100
const id = navigator.geolocation.watchPosition(
34-
(pos) => {
35-
callback(pos);
101+
pos => {
102+
callback(this.augmentPosition(pos));
36103
},
37-
(err) => {
104+
err => {
38105
callback(null, err);
39106
},
40107
{

0 commit comments

Comments
 (0)