Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions spec/L.Control.Locate.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,52 @@ describe("LocateControl", () => {
assert.strictEqual(control._compassHeading, 270, "Should process orientation event (360 - 90)");
});

it("should process iOS heading when webkitCompassHeading is 0", () => {
const control = new LocateControl({ showCompass: true });
map.addControl(control);
control._active = true;

control._onDeviceOrientation({ webkitCompassHeading: 0, alpha: null });

assert.strictEqual(control._compassHeading, 0, "Should treat 0 as a valid iOS heading");
});

it("should compensate iOS heading with screen orientation angle", () => {
const control = new LocateControl({ showCompass: true });
map.addControl(control);
control._active = true;

const originalOrientation = window.screen?.orientation;

try {
window.screen.orientation = { angle: 90, type: "landscape-primary" };

control._onDeviceOrientation({ webkitCompassHeading: 90, alpha: null });

assert.strictEqual(control._compassHeading, 180, "Should apply screen angle compensation on iOS");
} finally {
window.screen.orientation = originalOrientation;
}
});

it("should fallback to zero screen angle when orientation is unavailable", () => {
const control = new LocateControl({ showCompass: true });
map.addControl(control);
control._active = true;

const originalOrientation = window.screen?.orientation;

try {
window.screen.orientation = undefined;

control._onDeviceOrientation({ webkitCompassHeading: 90, alpha: null });

assert.strictEqual(control._compassHeading, 90, "Should keep iOS heading unchanged without screen orientation");
} finally {
window.screen.orientation = originalOrientation;
}
});

it("should prefer deviceorientationabsolute when available", async () => {
window.ondeviceorientationabsolute = null;

Expand Down
8 changes: 8 additions & 0 deletions spec/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ global.DeviceOrientationEvent = class DeviceOrientationEvent extends Event {
}
};

// Mock screen.orientation (not provided by happy-dom as a global)
global.screen = {
orientation: {
angle: 0,
type: "portrait-primary"
}
};

// Mock requestAnimationFrame
global.requestAnimationFrame = (callback) => {
return setTimeout(callback, 16);
Expand Down
8 changes: 5 additions & 3 deletions src/L.Control.Locate.js
Original file line number Diff line number Diff line change
Expand Up @@ -906,9 +906,11 @@ const LocateControl = Control.extend({
return;
}

if (e.webkitCompassHeading) {
// iOS
this._setCompassHeading(e.webkitCompassHeading);
if (e.webkitCompassHeading != null) {
// iOS: webkitCompassHeading is relative to device top.
// Compensate using current screen orientation when available.
const screenAngle = window.screen?.orientation?.angle ?? 0;
this._setCompassHeading((e.webkitCompassHeading + screenAngle) % 360);
} else if (e.alpha !== null) {
// Android
this._setCompassHeading(360 - e.alpha);
Expand Down