{"id":25536214,"url":"https://github.com/bhaskarblur/arcore-navigation-with-lat-long","last_synced_at":"2025-04-11T14:43:07.484Z","repository":{"id":170225616,"uuid":"646355404","full_name":"bhaskarblur/ArCore-Navigation-With-Lat-Long","owner":"bhaskarblur","description":"This repository developed in Native Android - Java demonstrates how to place objects \u0026 draw routes between them in ARCore scene using Latitude \u0026 Longitude","archived":false,"fork":false,"pushed_at":"2024-10-28T14:02:43.000Z","size":61748,"stargazers_count":11,"open_issues_count":1,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-25T10:51:09.511Z","etag":null,"topics":["anchor","android","android-studio","arcore-android","gps","hacktoberfest","hacktoberfest-accepted","hacktoberfest2023","indoor-navigation","indoor-positioning","java","latitude-and-longitude","navigation"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bhaskarblur.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-28T05:52:05.000Z","updated_at":"2025-02-24T13:08:03.000Z","dependencies_parsed_at":"2024-10-28T15:33:59.350Z","dependency_job_id":null,"html_url":"https://github.com/bhaskarblur/ArCore-Navigation-With-Lat-Long","commit_stats":null,"previous_names":["bhaskarblur/arcore-navigation-anchors-using-latlng-in-ar-world---android","bhaskarblur/arcore-navigation-with-lat-long"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bhaskarblur%2FArCore-Navigation-With-Lat-Long","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bhaskarblur%2FArCore-Navigation-With-Lat-Long/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bhaskarblur%2FArCore-Navigation-With-Lat-Long/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bhaskarblur%2FArCore-Navigation-With-Lat-Long/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bhaskarblur","download_url":"https://codeload.github.com/bhaskarblur/ArCore-Navigation-With-Lat-Long/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248420394,"owners_count":21100373,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anchor","android","android-studio","arcore-android","gps","hacktoberfest","hacktoberfest-accepted","hacktoberfest2023","indoor-navigation","indoor-positioning","java","latitude-and-longitude","navigation"],"created_at":"2025-02-20T04:30:54.730Z","updated_at":"2025-04-11T14:43:07.465Z","avatar_url":"https://github.com/bhaskarblur.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ARCore Navigation Anchors using LatLng in AR World - Android\nThis repository developed in Native Android - Java demonstrates how to place objects \u0026amp; draw routes between them in ARCore scene using Latitude \u0026amp; Longitude. The code doesn't require any use of Google Geospatial API or any such library.\n\nDeveloped by: Bhaskar Kaura (https://www.linkedin.com/in/bhaskar-kaura-300b09272/)\n[app](app)\n## How does this work?\nThis app uses ARCore with Sceneform to support AR \u0026 render objects in AR World. The current location of user is continuously calculated\nand also the yaw(azimuth) aka the rotation of the device around it's -z axis which is respective to north. Now, we have our current\nLatitude, Longitude \u0026 our rotation so we assume that we are an origin point on graph (0,0) and we added a Latitude \u0026 Longitude of a coordinate into the graph say it's (3,3). So, we convert this (3,3) via a CoordinateHelper class which converts LatLng into local coordinates using our current location, yaw and the target Location. Now we got the local coordinates, we set this as a LocalPosition\nto our AnchorNode in AR World.\n\n## Accuracy?\nThis solution is pretty accurate as it doesnt rely on any external system and just requires user location \u0026 yaw. But, keep in mind that if the location LatLng \u0026 yaw of user is incorrect or inaccurate, then this may affect the resulted coordinates.\n\n## Examples\n\n![Anchor placed in AR World using LatLng](https://github.com/bhaskarblur/ARCore-Navigation-Anchors-using-LatLng-in-AR-World---Android/blob/316265830063214b58c44a39c44988d7aa1859e5/poi.jpg)\n\nAnchor placed in AR World using LatLng\n\n![Route lines draw between different LatLng in AR World](https://github.com/bhaskarblur/ARCore-Navigation-Anchors-using-LatLng-in-AR-World---Android/blob/316265830063214b58c44a39c44988d7aa1859e5/route.jpg)\n\nRoute lines draw between different LatLng in AR World\n\n## With this app you will be able to:\n- Get current **user location \u0026 yaw**\n- Convert entered **Latitude \u0026 Longitude** into local coordinates\n- Place **anchor \u0026 object on LatLng** using coordinate converter in AR Scene\n- **Draw routes between different LatLng** using coordinate converter in AR Scene\n\n## Getting Started\n\n### Setup all the required dependencies\n```\nimplementation ('es.situm:situm-sdk:2.85.3@aar') {\n        transitive = true\n    }\nimplementation 'com.google.android.gms:play-services-maps:18.1.0'\nimplementation 'com.google.ar.sceneform.ux:sceneform-ux:1.17.1'\nimplementation 'com.github.appoly:ARCore-Location:1.2'\nimplementation 'com.google.android.gms:play-services-location:21.0.1'\nimplementation 'de.javagl:obj:0.2.1'\nimplementation 'com.google.ar:core:1.31.0'\nimplementation 'com.google.ar.sceneform:core:1.17.1'\n``` \n\n### Setup Google Cloud Console Account \u0026 enable ArCore in it \u0026 make an API Key\n### Add the required permissions and metadata of ArCore Api in Manifest file\n### Enable ViewBinding in buildfeatures in Build Gradle file\n\n## Get user location\nUsing the code below, retrieve \u0026 update the current user location\n\n```\nprivate LatLng currentLatLng =\n            new LatLng(0, 0);\n            \nprivate LocationManager locationManager;\n\nlocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);\n\n  locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0L, (float) 0, new LocationListener() {\n            @Override\n            public void onLocationChanged(@NonNull Location location) {\n\n                // this location LatLng can be highly inaccurate if being used in indoor location\n                // or places where it's hard to navigate, it is suggested to use special\n                // navigation systems like indoor navigation system for accurate results.\n\n                // this inaccurate latlng can affect the coordinates of poi anchors, for testing\n                // try with a fixed current latlng on which you are currently placed.\n\n                currentLatLng = new LatLng(location.getLatitude(), location.getLatitude());\n                binding.instrText.setText(\"Place your camera slightly next to your foot and tap on the \n                floor shadow to begin!\");\n\n\n            }\n        });\n```\n\n## Get user yaw or the rotation\nUsing the code below, retrieve the device yaw or the azimuth\n\n```\nprivate float I[] = null; //for magnetic rotational data\nprivate float accels[] = new float[3];\nprivate float mags[] = new float[3];\nprivate float[] values = new float[3];\nprivate float yaw;\nprivate float pitch;\nprivate float roll;\nprivate SensorManager sensorManager;\nprivate Sensor sensor;\n    \n//sensor manager \u0026 sensor required to calculate yaw\nsensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);\nsensor = sensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR);\n```\n\n\n\nInside OnResume() :\n\n```\n  Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);\n        if (accelerometer != null) {\n            sensorManager.registerListener(this, accelerometer,\n                    SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);\n        }\n        Sensor magneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);\n        if (magneticField != null) {\n            sensorManager.registerListener(this, magneticField,\n                    SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);\n        }\n        \n```        \n\n\n\nNow, implement the SensorEventListener in activity class and add these functions\n\n```\n@Override\n    public void onSensorChanged(SensorEvent sensorEvent) {\n        switch (sensorEvent.sensor.getType())\n        {\n            case Sensor.TYPE_MAGNETIC_FIELD:\n                mags = sensorEvent.values.clone();\n                break;\n            case Sensor.TYPE_ACCELEROMETER:\n                accels = sensorEvent.values.clone();\n                break;\n        }\n\n        if (mags != null \u0026\u0026 accels != null) {\n            Rot = new float[9];\n            I= new float[9];\n            SensorManager.getRotationMatrix(Rot, I, accels, mags);\n\n            // Correct if screen is in Landscape\n            float[] outR = new float[9];\n            SensorManager.remapCoordinateSystem(Rot, SensorManager.AXIS_X,SensorManager.AXIS_Z, outR);\n            SensorManager.getOrientation(outR, values);\n\n            // here we calculated the final yaw(azimuth), roll \u0026 pitch of the device.\n            // multiplied by a global standard value to get accurate results\n\n            // this is the yaw or the azimuth we need\n            yaw = values[0] * 57.2957795f;\n            pitch =values[1] * 57.2957795f;\n            roll = values[2] * 57.2957795f;\n\n            //retrigger the loop when things are repopulated\n            mags = null;\n            accels = null;\n        }\n    }\n\n```\n\n\n## Convert the Latitude \u0026 Longitude in Local Coordinates\nThis code will help you to convert your desired LatLng into local coordinates to place model on it. It uses\nCoordinatesHelper class to do conversion. **Make sure you call this code inside OnTapPlane Listener.**\n\n```\n     // instantiating our cordinates helper class with our current coordinate\n        CoordinatesHelper cartesianHelper=new CoordinatesHelper(currentLatLng);\n        cartesianHelper.setCenter_(currentLatLng);\n\n        // converting the LatLng coordinates of poi to local coordinates to get place in AR\n        Coordinate coordinate1 =\n                        cartesianHelper.GetLocalCoordinates(new LatLng(\n                               \"Latitude\", \"Longitude\"), yaw);\n                               \n```\n\n\n## Place POI on the converted Local Coordinate\nBelow, we used onTapPlane to get information about our environment and set a node inside it. You can instantly place object\nwithout tapping the plane by using OnUpdateListener of ArFragment ArFrame. **It is suggested to place first anchor on foot of user\nor beneath the camera for best results**\n\n```\n  arFragment.setOnTapArPlaneListener(new BaseArFragment.OnTapArPlaneListener() {\n            @Override\n            public void onTapPlane(HitResult hitResult, Plane plane, MotionEvent motionEvent) {\n                if(!poiPlaced) {\n                    // calling it here as we want to calculate coordinates only when tapped                  \n                    convertCoordinates(\"Latitude\", \"Longitude\");\n                    \n                    Anchor anchor= hitResult.createAnchor();\n                    AnchorNode anchorNode = new AnchorNode(anchor);\n\n                    // passing the coordinates of target from the list of coordinates,\n                    // in your case you can set your own lat long and convert it into local coordinates,\n                    // passing the anchorNode as the reference node to place other anchors in world.\n                    // make sure you point the ar camera on your foot and then tap on ar shadow to place anchor\n                    \n                    PlacePOIinRealWorld(coordinate1, anchorNode);\n\n                }\n            }\n        });\n        \n        \n ```  \n \n \n \n ```  \n   private void PlacePOIinRealWorld(CartesianCoordinate coordinate, AnchorNode reference) {\n\n        // prepare renderable model for POI\n        ModelRenderable.builder().\n                setSource(\n                        ARActivity.this,\n                        RenderableSource\n                                .builder()\n                                .setSource(ARActivity.this, Uri.parse(\n                                                poi_uri)\n                                        , RenderableSource.SourceType.GLTF2)\n                                .setScale(.3f)\n                                .build())\n                .setRegistryId(poi_uri)\n                .build()\n                .thenAccept(modelRenderable -\u003e addPOIinScene( modelRenderable,reference, coordinate ))\n                .exceptionally(throwable -\u003e {\n                    Toast.makeText(ARActivity.this, \"error:\"+throwable.getCause(), Toast.LENGTH_SHORT).show();\n                    return null;\n                });\n\n        poiPlaced=true;\n        binding.instrText.setVisibility(View.GONE);\n    }\n\n    private void addPOIinScene(ModelRenderable modelRenderable, AnchorNode reference, CartesianCoordinate targetCoordinate_) {\n\n        AnchorNode anchorNode1=new AnchorNode(null);\n        anchorNode1.setRenderable(modelRenderable);\n        arFragment.getArSceneView().getScene().addChild(anchorNode1);\n\n        CartesianCoordinate Singlecoordinate= targetCoordinate_;\n\n        //set the position of your POI model based on the coordinates calculated by\n        // the coordinate convertor\n        anchorNode1.setLocalPosition(new Vector3(\n                (float) (reference.getLocalPosition().x+ Singlecoordinate.getX()),\n                reference.getLocalPosition().y+.15f,\n                (float) (reference.getLocalPosition().z-Singlecoordinate.getY())\n        ));\n\n        //set rotation if needed (optional)\n        anchorNode1.setLocalRotation(Quaternion.axisAngle(new Vector3(0f, -1f, 0f), 30f));\n\n        Toast.makeText(ARActivity.this, \"POI added in scene.\", Toast.LENGTH_SHORT).show();\n\n        poiPlaced= true;\n        \n    }\n\n```  \n \n \n## Draw routes between different LatLng with the converted Local Coordinate\nBelow, we used onTapPlane to get information about our environment and set a node inside it. You can instantly place object\nwithout tapping the plane by using OnUpdateListener of ArFragment ArFrame. **It is suggested to place first anchor on foot of user\nor beneath the camera for best results**\n\n``` \narFragment.setOnTapArPlaneListener(new BaseArFragment.OnTapArPlaneListener() {\n            @Override\n            public void onTapPlane(HitResult hitResult, Plane plane, MotionEvent motionEvent) {\n                if(!routePlaced) {\n                    // calling it here as we want to calculate coordinates only when tapped\n                    addDatainPOIList();\n                    Anchor anchor= hitResult.createAnchor();\n                    AnchorNode anchorNode = new AnchorNode(anchor);\n\n                    DrawLinesBetweenPOIs(anchorNode, false);\n\n                    for(int i=0; i\u003ccoordinateList.size(); i++) {\n                        AnchorNode repNode= new AnchorNode(null);\n                        repNode.setLocalPosition(\n                                new Vector3((float) (anchorNode.getLocalPosition()\n                                        .x + coordinateList.get(i).getX()), anchorNode.getLocalPosition()\n                                        .y, (float) (anchorNode.getLocalPosition()\n                                        .z - coordinateList.get(i).getY())));\n\n                        DrawLinesBetweenPOIs(repNode, true);\n                    }\n                    routePlaced=true;\n                    binding.instrText.setVisibility(View.GONE);\n                }\n            }\n        });\n        \n``` \n\n``` \nprivate AnchorNode lastAnchorNode = new AnchorNode();\n\nprivate void DrawLinesBetweenPOIs(AnchorNode anchorNode, boolean place_) {\n        if (lastAnchorNode != null) {\n            anchorNode.setParent(arFragment.getArSceneView().getScene());\n            Vector3 point1, point2;\n            point1 = lastAnchorNode.getWorldPosition();\n            point2 = anchorNode.getWorldPosition();\n\n            if(place_) {\n                final Vector3 difference = Vector3.subtract(point1, point2);\n                final Vector3 directionFromTopToBottom = difference.normalized();\n                final Quaternion rotationFromAToB =\n                        Quaternion.lookRotation(directionFromTopToBottom, Vector3.up());\n                Texture.Sampler sampler = Texture.Sampler.builder()\n                        .setMinFilter(Texture.Sampler.MinFilter.LINEAR_MIPMAP_LINEAR)\n                        .setMagFilter(Texture.Sampler.MagFilter.LINEAR)\n                        .build();\n\n                Texture.builder()\n                        .setSource(() -\u003e getApplicationContext().getAssets().open(\"arrow_texture.png\"))\n                        .setSampler(sampler)\n                        .build().thenAccept(texture -\u003e {\n                            MaterialFactory.makeTransparentWithTexture(getApplicationContext(), texture) //new Color(0, 255, 244))\n                                    .thenAccept(\n                                            material -\u003e {\n\n                                                ModelRenderable model = ShapeFactory.makeCube(\n                                                        new Vector3(.3f, .006f, difference.length()),\n                                                        Vector3.zero(), material);\n\n\n                                                AnchorNode node = new AnchorNode();\n                                                node.setParent(anchorNode);\n                                                node.setRenderable(model);\n                                                node.setWorldPosition(Vector3.add(point1, point2).scaled(.5f));\n                                                node.setWorldRotation(rotationFromAToB);\n\n                                            }\n                                    ).exceptionally(new Function\u003cThrowable, Void\u003e() {\n                                        @Override\n                                        public Void apply(Throwable throwable) {\n                                            Toast.makeText(ARActivity.this, \"Error: \"+\n                                                    throwable.getMessage().toString(), Toast.LENGTH_SHORT).show();\n                                            return null;\n                                        }\n                                    });\n                        });\n\n            }\n\n            Log.d(\"position:\",anchorNode.getLocalPosition().toString());\n            lastAnchorNode = anchorNode;\n\n        }\n    }\n    \n``` \n\n## All done, you're ready to go!\nThank you for viewing my repository, i hope it helped you achieve your desired results\n\n## Let's Connect\nYou can find me here:\nLinkedin: [Link](https://www.linkedin.com/in/bhaskar-kaura-300b09272/)\nInstagram: [Link](https://www.instagram.com/bhaskar_blur/)\nTwitter: [Link](https://twitter.com/Bhaskar_blur)\n\n## Support information\nFor any question or bug report, please send an email to [bhaskar7005blur@gmail.com](mailto:bhaskar7005blur@gmail.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbhaskarblur%2Farcore-navigation-with-lat-long","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbhaskarblur%2Farcore-navigation-with-lat-long","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbhaskarblur%2Farcore-navigation-with-lat-long/lists"}