5454#include < VROImageAndroid.h>
5555#include < algorithm>
5656#include < cmath>
57+ #include < cstdio>
58+ #include < fcntl.h>
59+ #include < unistd.h>
5760
5861static bool kDebugTracking = false ;
5962
63+ // Generate a random UUID v4 string (xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx).
64+ static std::string generateUUID () {
65+ uint8_t b[16 ];
66+ int fd = open (" /dev/urandom" , O_RDONLY);
67+ if (fd >= 0 ) {
68+ read (fd, b, sizeof (b));
69+ close (fd);
70+ } else {
71+ // Fallback: zero-filled (shouldn't happen on Android).
72+ memset (b, 0 , sizeof (b));
73+ }
74+ b[6 ] = (b[6 ] & 0x0f ) | 0x40 ; // version 4
75+ b[8 ] = (b[8 ] & 0x3f ) | 0x80 ; // variant bits
76+ char buf[37 ];
77+ snprintf (buf, sizeof (buf),
78+ " %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" ,
79+ b[0 ],b[1 ],b[2 ],b[3 ], b[4 ],b[5 ], b[6 ],b[7 ],
80+ b[8 ],b[9 ], b[10 ],b[11 ],b[12 ],b[13 ],b[14 ],b[15 ]);
81+ return std::string (buf);
82+ }
83+
6084// Minimum plane size filter to prevent small artifacts from being detected
6185// Planes smaller than this threshold will be ignored
6286static const float kMinPlaneExtent = 0 .10f ; // 10cm minimum size in any dimension
@@ -1410,8 +1434,7 @@ void VROARSessionARCore::setGeospatialAnchorProvider(VROGeospatialAnchorProvider
14101434
14111435 if (provider == VROGeospatialAnchorProvider::ReactVision) {
14121436 // ReactVision has no ARCore dependency — no VPS, no ARCore session config.
1413- // GPS→AR placement (createGeospatialAnchor) is not supported for this provider.
1414- // Use rvCreateGeospatialAnchor / rvFindNearbyGeospatialAnchors for RVCA metadata.
1437+ // GPS→AR placement uses createLocalGPSAnchor; backend persistence via createAnchor.
14151438 // _geospatialProviderRV is initialized in setReactVisionConfig().
14161439 if (!_geospatialProviderRV) {
14171440 pwarn (" VROARSessionARCore: ReactVision geospatial not ready — "
@@ -1552,7 +1575,8 @@ static std::shared_ptr<VROGeospatialAnchor> createLocalGPSAnchor(
15521575 VROGeospatialAnchorType type, VROQuaternion quaternion,
15531576 std::shared_ptr<VROARSessionARCore> arSession,
15541577 ReactVisionCCA::RVCCAGeospatialProvider *provider,
1555- std::string &outError) {
1578+ std::string &outError,
1579+ const std::string &knownId = " " ) {
15561580 if (devicePose.latitude == 0.0 && devicePose.longitude == 0.0 ) {
15571581 outError = " GPS position not available yet. Ensure location permissions are granted." ;
15581582 return nullptr ;
@@ -1573,12 +1597,19 @@ static std::shared_ptr<VROGeospatialAnchor> createLocalGPSAnchor(
15731597 }
15741598 std::shared_ptr<arcore::Anchor> anchorShared (nativeAnchor);
15751599 std::string key = VROStringUtil::toString64 (anchorShared->getId ());
1600+ std::string uuid = knownId.empty () ? generateUUID () : knownId;
15761601
15771602 auto geoAnchor = std::make_shared<VROGeospatialAnchor>(type, anchorLat, anchorLng, anchorAlt, quaternion);
1578- geoAnchor->setId (key );
1603+ geoAnchor->setId (uuid );
15791604 geoAnchor->setResolveState (VROGeospatialAnchorResolveState::Success);
15801605
1581- auto vAnchor = std::make_shared<VROARAnchorARCore>(key, anchorShared, geoAnchor, arSession);
1606+ // Set the AR-space transform immediately so the onSuccess callback gets the correct position.
1607+ // (processUpdatedAnchors will keep it in sync each frame via updateFromGeospatialTransform.)
1608+ float mtx[16 ];
1609+ nativeAnchor->getTransform (mtx);
1610+ geoAnchor->setTransform (VROMatrix4f (mtx));
1611+
1612+ auto vAnchor = std::make_shared<VROARAnchorARCore>(uuid, anchorShared, geoAnchor, arSession);
15821613 arSession->addAnchor (vAnchor);
15831614 return geoAnchor;
15841615}
@@ -1593,7 +1624,8 @@ void VROARSessionARCore::createGeospatialAnchor(double latitude, double longitud
15931624 auto anchor = createLocalGPSAnchor (_session, _lastKnownGPSPose,
15941625 latitude, longitude, altitude,
15951626 VROGeospatialAnchorType::WGS84, quaternion,
1596- shared_from_this (), _geospatialProviderRV.get (), error);
1627+ shared_from_this (), _geospatialProviderRV.get (),
1628+ error);
15971629 if (anchor) { if (onSuccess) onSuccess (anchor); }
15981630 else { if (onFailure) onFailure (error); }
15991631 return ;
@@ -1633,6 +1665,74 @@ void VROARSessionARCore::createGeospatialAnchor(double latitude, double longitud
16331665 }
16341666}
16351667
1668+ void VROARSessionARCore::resolveGeospatialAnchor (const std::string& platformUuid,
1669+ VROQuaternion quaternion,
1670+ std::function<void (std::shared_ptr<VROGeospatialAnchor>)> onSuccess,
1671+ std::function<void(std::string error)> onFailure) {
1672+ #if RVCCA_AVAILABLE
1673+ if (getGeospatialAnchorProvider () == VROGeospatialAnchorProvider::ReactVision) {
1674+ if (!_geospatialProviderRV) {
1675+ if (onFailure) onFailure (" ReactVision geospatial provider not initialized" );
1676+ return ;
1677+ }
1678+ if (_lastKnownGPSPose.latitude == 0.0 && _lastKnownGPSPose.longitude == 0.0 ) {
1679+ if (onFailure) onFailure (" GPS position not available yet. Ensure location permissions are granted." );
1680+ return ;
1681+ }
1682+ VROGeospatialPose devicePose = _lastKnownGPSPose;
1683+ std::weak_ptr<VROARSessionARCore> weakSelf = shared_from_this ();
1684+ _geospatialProviderRV->getAnchor (platformUuid,
1685+ [weakSelf, devicePose, platformUuid, quaternion, onSuccess, onFailure]
1686+ (ReactVisionCCA::ApiResult<ReactVisionCCA::GeospatialAnchorRecord> r) {
1687+ VROPlatformDispatchAsyncRenderer ([weakSelf, r, devicePose, platformUuid, quaternion, onSuccess, onFailure] {
1688+ auto self = weakSelf.lock ();
1689+ if (!self) return ;
1690+ if (!r.success ) {
1691+ if (onFailure) onFailure (r.error .message );
1692+ return ;
1693+ }
1694+ std::string error;
1695+ auto anchor = createLocalGPSAnchor (self->_session , devicePose,
1696+ r.data .lat , r.data .lng , r.data .alt ,
1697+ VROGeospatialAnchorType::WGS84, quaternion,
1698+ self, self->_geospatialProviderRV .get (),
1699+ error, platformUuid);
1700+ if (anchor) { if (onSuccess) onSuccess (anchor); }
1701+ else { if (onFailure) onFailure (error); }
1702+ });
1703+ });
1704+ return ;
1705+ }
1706+ #endif
1707+ if (onFailure) onFailure (" resolveGeospatialAnchor requires ReactVision geospatial provider" );
1708+ }
1709+
1710+ void VROARSessionARCore::hostGeospatialAnchor (double latitude, double longitude, double altitude,
1711+ const std::string& altitudeMode,
1712+ std::function<void (std::string)> onSuccess,
1713+ std::function<void(std::string)> onFailure) {
1714+ #if RVCCA_AVAILABLE
1715+ if (getGeospatialAnchorProvider () == VROGeospatialAnchorProvider::ReactVision) {
1716+ if (!_geospatialProviderRV) {
1717+ if (onFailure) onFailure (" ReactVision geospatial provider not initialized" );
1718+ return ;
1719+ }
1720+ ReactVisionCCA::GeospatialCreateRequest req;
1721+ req.lat = latitude;
1722+ req.lng = longitude;
1723+ req.alt = altitude;
1724+ req.altitudeMode = altitudeMode.empty () ? " street_level" : altitudeMode;
1725+ _geospatialProviderRV->createAnchor (req,
1726+ [onSuccess, onFailure](ReactVisionCCA::ApiResult<ReactVisionCCA::GeospatialAnchorRecord> r) {
1727+ if (r.success ) { if (onSuccess) onSuccess (r.data .id ); }
1728+ else { if (onFailure) onFailure (r.error .message ); }
1729+ });
1730+ return ;
1731+ }
1732+ #endif
1733+ if (onFailure) onFailure (" hostGeospatialAnchor requires ReactVision geospatial provider" );
1734+ }
1735+
16361736void VROARSessionARCore::createTerrainAnchor (double latitude, double longitude, double altitudeAboveTerrain,
16371737 VROQuaternion quaternion,
16381738 std::function<void (std::shared_ptr<VROGeospatialAnchor>)> onSuccess,
@@ -1719,7 +1819,7 @@ void VROARSessionARCore::createRooftopAnchor(double latitude, double longitude,
17191819 }
17201820
17211821 std::weak_ptr<VROARSessionARCore> weakSession = shared_from_this ();
1722-
1822+
17231823 _session->createRooftopAnchor (latitude, longitude, altitudeAboveRooftop, quaternion.X , quaternion.Y , quaternion.Z , quaternion.W ,
17241824 [weakSession, latitude, longitude, altitudeAboveRooftop, quaternion, onSuccess](arcore::Anchor *nativeAnchor) {
17251825 VROPlatformDispatchAsyncRenderer ([weakSession, latitude, longitude, altitudeAboveRooftop, quaternion, onSuccess, nativeAnchor] {
@@ -1760,17 +1860,17 @@ void VROARSessionARCore::createRooftopAnchor(double latitude, double longitude,
17601860void VROARSessionARCore::removeGeospatialAnchor (std::shared_ptr<VROGeospatialAnchor> anchor) {
17611861 if (!anchor) return ;
17621862
1763- #if RVCCA_AVAILABLE
1764- if (getGeospatialAnchorProvider () == VROGeospatialAnchorProvider::ReactVision
1765- && _geospatialProviderRV) {
1766- _geospatialProviderRV->deleteAnchor (anchor->getId (),
1767- [](bool , ReactVisionCCA::ApiError) { /* fire-and-forget */ });
1768- }
1769- #endif
1863+ // Note: do NOT call _geospatialProviderRV->deleteAnchor() here.
1864+ // removeGeospatialAnchor only removes the anchor from the local AR session.
1865+ // Use rvDeleteGeospatialAnchor() to explicitly delete from the backend.
17701866
1867+ // Search by ID because the caller may pass a dummy anchor (from nativeRemoveGeospatialAnchor)
1868+ // that shares only the ID with the stored trackable, not the same pointer.
17711869 std::shared_ptr<VROARAnchorARCore> foundAnchor = nullptr ;
1870+ const std::string &targetId = anchor->getId ();
17721871 for (std::shared_ptr<VROARAnchorARCore> vAnchor : _anchors) {
1773- if (vAnchor->getTrackable () == anchor) {
1872+ auto trackable = vAnchor->getTrackable ();
1873+ if (trackable && trackable->getId () == targetId) {
17741874 foundAnchor = vAnchor;
17751875 break ;
17761876 }
0 commit comments