webxr-geospatial
v1.0.1
Published
Android companion localization and WGS84 coordinate utilities for WebXR
Maintainers
Readme
webxr-geospatial
TypeScript utilities for using an Android-provided ARCore Geospatial pose in a WebXR application.
English
webxr-geospatial connects a browser application with a compatible Android companion app and converts WGS84 positions into heading-aligned local coordinates suitable for a WebXR scene.
The package:
- launches the companion app and supplies a browser return URL;
- reads and validates the returned Geospatial pose;
- removes localization data from the visible URL;
- converts WGS84 coordinates through ECEF and ENU into local metre-based coordinates;
- applies yaw from the ARCore East-Up-South quaternion while keeping the vertical axis upright;
- derives a framework-independent yaw-only scene alignment from an initial WebXR viewer pose.
It is framework-independent, ships as an ES module with TypeScript declarations, and has no runtime dependencies.
Companion app
The localization flow is designed for use with the WebXR Geospatial Companion Android app. The app obtains the ARCore Geospatial pose and returns it to the browser. It is not included in this npm package and is not linked here because no public app repository is available yet.
The coordinate conversion utilities can also be used independently when a compatible GeospatialPose is available from another source.
Installation
npm install webxr-geospatialQuick start
Register the pose handler when the page loads. Start localization from a user interaction so the browser is allowed to open the Android app.
import { createLocalCoordinateTransformer, handleGeospatialPose, startGeospatialLocalization } from "webxr-geospatial";
handleGeospatialPose((pose) => {
const transformer = createLocalCoordinateTransformer(pose);
// GeoJSON order: longitude, latitude, ellipsoidal altitude
const [x, y, z] = transformer.toLocalPosition([13.52584808, 52.45678137, 74.189]);
console.log("Local position:", { x, y, z });
});
document.querySelector("#locate")?.addEventListener("click", () => {
startGeospatialLocalization();
});The returned coordinates are relative to the Geospatial pose. The consuming application remains responsible for placing this local coordinate system into its actual WebXR reference space, for example by aligning it with the first WebXR viewer pose.
Public API
startGeospatialLocalization(options?)
Opens the Android companion app and supplies the URL to which it should return. Call it directly from a user interaction.
startGeospatialLocalization();All configuration values are optional:
startGeospatialLocalization({
returnUrl: window.location.href,
companionAppLink: "https://example.com/android/locate/",
companionPackageId: "com.example.companion",
installFallbackUrl: "https://example.com/android/install/",
});| Option | Purpose |
| -------------------- | ------------------------------------------------------------------------------------------------------------- |
| returnUrl | Browser URL reopened after localization. Defaults to the current URL. |
| companionAppLink | HTTPS App Link handled by the companion app. |
| companionPackageId | Android package ID used to target the launch intent. |
| installFallbackUrl | Page opened when the companion app cannot be launched. Defaults to /android/install/ on the current origin. |
Return URLs must use HTTPS. HTTP is accepted only for loopback hosts during local development.
handleGeospatialPose(callback)
Synchronously reads a returned pose from the URL fragment, removes the localization fragment, validates the payload, and invokes the callback when the pose is valid.
handleGeospatialPose((pose) => {
console.log(pose.latitude, pose.longitude, pose.altitude);
});The callback receives:
interface GeospatialPose {
latitude: number;
longitude: number;
altitude: number;
quaternion: readonly [number, number, number, number];
}altitude is measured in metres above the WGS84 ellipsoid. quaternion is the ARCore East-Up-South quaternion in [x, y, z, w] order.
createLocalCoordinateTransformer(origin)
Creates a reusable transformer whose origin and horizontal heading are defined by a Geospatial pose.
const transformer = createLocalCoordinateTransformer(pose);
console.log(transformer.headingRadians);transformer.toLocalPosition(position)
Converts a WGS84 position in GeoJSON order into local coordinates in metres.
const localPosition = transformer.toLocalPosition([longitude, latitude, ellipsoidalAltitude]);The result is [x, y, z]: positive X points right, positive Y points up, and negative Z points forward relative to the origin pose.
headingRadiansFromEastUpSouthQuaternion(quaternion)
Optionally extracts the signed horizontal heading in radians without creating a coordinate transformer.
import { headingRadiansFromEastUpSouthQuaternion } from "webxr-geospatial";
const heading = headingRadiansFromEastUpSouthQuaternion(pose.quaternion);createYawOnlyViewerAlignment(transform)
Derives the position and horizontal rotation for placing the local geospatial scene at an initial WebXR viewer pose. Pitch and roll are discarded so the scene remains vertical. The result is independent of Three.js or any other rendering framework.
import { createYawOnlyViewerAlignment } from "webxr-geospatial";
const alignment = createYawOnlyViewerAlignment({
position: [viewerPosition.x, viewerPosition.y, viewerPosition.z],
quaternion: [viewerQuaternion.x, viewerQuaternion.y, viewerQuaternion.z, viewerQuaternion.w],
});
sceneRoot.position.set(...alignment.position);
sceneRoot.rotation.set(0, alignment.yawRadians, 0);Platform notes
- The Android localization flow requires a compatible companion app and an Android browser that can launch Android intents.
- Production return URLs require HTTPS; loopback HTTP is supported for development.
- Coordinate conversion does not require browser globals and can be used separately from the Android flow.
- Node.js 20 or newer is required when the package is consumed in Node-based tooling.
Privacy
The companion app returns the pose in the URL fragment, which is not included in the HTTP request to the web server. The package removes this fragment before delivering a valid pose to application code. Applications should still treat location data as sensitive and avoid unnecessary logging or persistence.
Deutsch
webxr-geospatial verbindet eine Browseranwendung mit einer kompatiblen Android-Companion-App und rechnet WGS84-Positionen in horizontal ausgerichtete lokale Koordinaten für eine WebXR-Szene um.
Das Paket:
- startet die Companion-App und übergibt ihr eine Rücksprungadresse;
- liest und prüft die zurückgegebene Geospatial Pose;
- entfernt die Lokalisierungsdaten aus der sichtbaren URL;
- rechnet WGS84-Koordinaten über ECEF und ENU in lokale metrische Koordinaten um;
- berücksichtigt den Yaw-Winkel der ARCore-East-Up-South-Quaternion, ohne die vertikale Achse zu neigen;
- bestimmt aus einer initialen WebXR-Viewer-Pose eine frameworkneutrale horizontale Szenenausrichtung.
Es ist unabhängig von einem bestimmten Webframework, wird als ES-Modul mit TypeScript-Typdefinitionen ausgeliefert und besitzt keine Laufzeitabhängigkeiten.
Companion-App
Der Lokalisierungsablauf ist für die Verwendung mit der Android-App WebXR Geospatial Companion vorgesehen. Die App bestimmt die ARCore Geospatial Pose und gibt sie an den Browser zurück. Sie ist nicht Bestandteil dieses npm-Pakets und wird hier nicht verlinkt, da derzeit noch kein öffentliches Repository für die App verfügbar ist.
Die Koordinatenumrechnung kann unabhängig davon verwendet werden, wenn eine kompatible GeospatialPose aus einer anderen Quelle vorliegt.
Installation
npm install webxr-geospatialSchnellstart
Der Pose-Handler sollte beim Laden der Webseite registriert werden. Die Lokalisierung muss durch eine Nutzereingabe gestartet werden, damit der Browser die Android-App öffnen darf.
import { createLocalCoordinateTransformer, handleGeospatialPose, startGeospatialLocalization } from "webxr-geospatial";
handleGeospatialPose((pose) => {
const transformer = createLocalCoordinateTransformer(pose);
// GeoJSON-Reihenfolge: Längengrad, Breitengrad, ellipsoidische Höhe
const [x, y, z] = transformer.toLocalPosition([13.52584808, 52.45678137, 74.189]);
console.log("Lokale Position:", { x, y, z });
});
document.querySelector("#locate")?.addEventListener("click", () => {
startGeospatialLocalization();
});Die ausgegebenen Koordinaten beziehen sich auf die Geospatial Pose. Die WebXR-Anwendung muss dieses lokale Koordinatensystem anschließend mit ihrem tatsächlichen WebXR-Referenzraum verbinden, beispielsweise anhand der ersten WebXR-Viewer-Pose.
Öffentliche Schnittstelle
startGeospatialLocalization(options?)
Öffnet die Android-Companion-App und übergibt die Adresse, zu der sie anschließend zurückkehren soll. Die Methode muss unmittelbar durch eine Nutzereingabe aufgerufen werden.
startGeospatialLocalization();Alle Einstellungen sind optional:
startGeospatialLocalization({
returnUrl: window.location.href,
companionAppLink: "https://example.com/android/locate/",
companionPackageId: "com.example.companion",
installFallbackUrl: "https://example.com/android/install/",
});| Option | Aufgabe |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| returnUrl | Nach der Lokalisierung erneut geöffnete Browseradresse. Standardmäßig wird die aktuelle Adresse verwendet. |
| companionAppLink | HTTPS-App-Link, der von der Companion-App verarbeitet wird. |
| companionPackageId | Android-Paketkennung zur gezielten Adressierung der App. |
| installFallbackUrl | Seite, die geöffnet wird, wenn die Companion-App nicht gestartet werden kann. Standardmäßig wird /android/install/ auf der aktuellen Origin verwendet. |
Rücksprungadressen müssen HTTPS verwenden. HTTP wird während der lokalen Entwicklung ausschließlich für Loopback-Adressen akzeptiert.
handleGeospatialPose(callback)
Liest synchron eine zurückgegebene Pose aus dem URL-Fragment, entfernt das Lokalisierungsfragment, prüft die enthaltenen Daten und ruft bei einer gültigen Pose den Callback auf.
handleGeospatialPose((pose) => {
console.log(pose.latitude, pose.longitude, pose.altitude);
});Der Callback erhält:
interface GeospatialPose {
latitude: number;
longitude: number;
altitude: number;
quaternion: readonly [number, number, number, number];
}altitude bezeichnet die Höhe in Metern über dem WGS84-Ellipsoid. quaternion ist die ARCore-East-Up-South-Quaternion in der Reihenfolge [x, y, z, w].
createLocalCoordinateTransformer(origin)
Erzeugt einen wiederverwendbaren Transformator, dessen Ursprung und horizontale Ausrichtung durch eine Geospatial Pose festgelegt werden.
const transformer = createLocalCoordinateTransformer(pose);
console.log(transformer.headingRadians);transformer.toLocalPosition(position)
Rechnet eine WGS84-Position in GeoJSON-Reihenfolge in lokale Koordinaten in Metern um.
const localPosition = transformer.toLocalPosition([longitude, latitude, ellipsoidalAltitude]);Das Ergebnis besitzt die Form [x, y, z]: Die positive X-Achse zeigt nach rechts, die positive Y-Achse nach oben und die negative Z-Achse nach vorne relativ zur Ursprungspose.
headingRadiansFromEastUpSouthQuaternion(quaternion)
Bestimmt optional die horizontale Ausrichtung in Radiant, ohne dafür einen Koordinatentransformator zu erzeugen.
import { headingRadiansFromEastUpSouthQuaternion } from "webxr-geospatial";
const heading = headingRadiansFromEastUpSouthQuaternion(pose.quaternion);createYawOnlyViewerAlignment(transform)
Bestimmt Position und horizontale Drehung, mit denen die lokale Geospatial-Szene an einer initialen WebXR-Viewer-Pose platziert wird. Pitch und Roll werden verworfen, damit die Szene vertikal bleibt. Das Ergebnis ist unabhängig von Three.js oder einem anderen Rendering-Framework.
import { createYawOnlyViewerAlignment } from "webxr-geospatial";
const alignment = createYawOnlyViewerAlignment({
position: [viewerPosition.x, viewerPosition.y, viewerPosition.z],
quaternion: [viewerQuaternion.x, viewerQuaternion.y, viewerQuaternion.z, viewerQuaternion.w],
});
sceneRoot.position.set(...alignment.position);
sceneRoot.rotation.set(0, alignment.yawRadians, 0);Plattformhinweise
- Der Android-Lokalisierungsablauf benötigt eine kompatible Companion-App und einen Android-Browser, der Android-Intents öffnen kann.
- Produktive Rücksprungadressen benötigen HTTPS; Loopback-HTTP wird für die lokale Entwicklung unterstützt.
- Die Koordinatenumrechnung benötigt keine Browser-APIs und kann unabhängig vom Android-Ablauf verwendet werden.
- Bei einer Verwendung in Node-basierten Werkzeugen wird Node.js 20 oder neuer benötigt.
Datenschutz
Die Companion-App gibt die Pose über das URL-Fragment zurück, das nicht Bestandteil der HTTP-Anfrage an den Webserver ist. Das Paket entfernt dieses Fragment, bevor eine gültige Pose an den Anwendungscode übergeben wird. Standortdaten sollten dennoch als sensible Daten behandelt und nicht unnötig protokolliert oder gespeichert werden.
License / Lizenz
MIT
