I am Write a code for Area measurement app in Android using with Google AR Core. To measure the area of a location easy with your mobile phone. connect to your first point to close the shape. You’ll find the area on the right. You can measurement area calculator determines the area of a number of common shapes, including rectangle, triangle, trapezoid, circle, sector, ellipse, and parallelogram.
So Lets Start to make a Area measurement app in Android. And used the latest google AR Core dependencies. Start your android studio and create a new project using with Kotlin language.
Area measurement app in Android.
Least make a android app for area measurement to calculate root top area surface area. Room area etc. core. add below AR Core dependencies in your Gradle app file.
Google AR Core dependencies
dependencies { implementation 'androidx.core:core-ktx:1.8.0' implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'com.google.android.material:material:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'com.google.ar:core:1.46.0' implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.17.1' implementation 'com.google.ar.sceneform:core:1.17.1' implementation 'com.google.ar.sceneform:assets:1.17.1' implementation "com.google.ar.sceneform:animation:1.17.1"
after adding sync your project. and fine the below activity_main.xml file source code.
activity_main.xml Complete Source Code.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:id="@+id/arFragment" android:name="com.google.ar.sceneform.ux.ArFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> <Button android:id="@+id/calculateButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Calculate Area" android:layout_margin="16dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" /> <ImageButton android:id="@+id/undoButton" android:layout_width="48dp" android:layout_height="48dp" android:src="@drawable/ic_clear" android:layout_margin="16dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt file Complete Source Code.
import android.os.Bundle import android.view.MotionEvent import android.widget.Button import android.widget.ImageButton import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import com.google.ar.core.Anchor import com.google.ar.core.HitResult import com.google.ar.core.Plane import com.google.ar.sceneform.AnchorNode import com.google.ar.sceneform.Node import com.google.ar.sceneform.math.Vector3 import com.google.ar.sceneform.rendering.MaterialFactory import com.google.ar.sceneform.rendering.ShapeFactory import com.google.ar.sceneform.rendering.ViewRenderable import com.google.ar.sceneform.ux.ArFragment import kotlin.math.abs import kotlin.math.sqrt class RoofToopArea : AppCompatActivity() { private lateinit var arFragment: ArFragment private lateinit var calculateButton: Button private lateinit var undoButton: ImageButton private val anchorNodes = mutableListOf<AnchorNode>() private val points = mutableListOf<Vector3>() private val measurementNodes = mutableListOf<Node>() private val verticalLines = mutableListOf<Node>() private var selectedNodeIndex: Int = -1 private var isMovingPoint = false private var areaLabelNode: Node? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_roof_toop_area) arFragment = supportFragmentManager.findFragmentById(R.id.arFragment) as ArFragment calculateButton = findViewById(R.id.calculateButton) undoButton = findViewById(R.id.undoButton) setupAR() setupButtons() } private fun setupAR() { arFragment.setOnTapArPlaneListener { hitResult, _, _ -> if (!isMovingPoint) { addPoint(hitResult) } } arFragment.arSceneView.scene.setOnTouchListener { hitTestResult, motionEvent -> when (motionEvent.action) { MotionEvent.ACTION_DOWN -> { val hitNode = hitTestResult.node val nodeIndex = anchorNodes.indexOfFirst { it == hitNode?.parent } if (nodeIndex != -1) { selectedNodeIndex = nodeIndex isMovingPoint = true return@setOnTouchListener true } } MotionEvent.ACTION_MOVE -> { if (isMovingPoint && selectedNodeIndex != -1) { moveSelectedPoint(motionEvent) return@setOnTouchListener true } } MotionEvent.ACTION_UP -> { if (isMovingPoint) { isMovingPoint = false selectedNodeIndex = -1 return@setOnTouchListener true } } } false } } private fun setupButtons() { calculateButton.setOnClickListener { if (points.size >= 3) { val area = calculatePolygonArea(points) showAreaLabel(area) } } undoButton.setOnClickListener { undoLastPoint() } } private fun addPoint(hitResult: HitResult) { val anchor = hitResult.createAnchor() MaterialFactory.makeOpaqueWithColor(this, com.google.ar.sceneform.rendering.Color(1f, 0.8f, 0f)) .thenAccept { material -> val anchorNode = AnchorNode(anchor) val sphere = ShapeFactory.makeSphere(0.02f, Vector3.zero(), material) anchorNode.renderable = sphere anchorNode.setParent(arFragment.arSceneView.scene) anchorNodes.add(anchorNode) points.add(anchorNode.worldPosition) addVerticalLine(anchorNode.worldPosition) updateMeasurements() } } private fun moveSelectedPoint(motionEvent: MotionEvent) { val frame = arFragment.arSceneView.arFrame if (frame != null) { val hitResult = frame.hitTest(motionEvent.x, motionEvent.y) hitResult.firstOrNull { hit -> val trackable = hit.trackable trackable is Plane && trackable.isPoseInPolygon(hit.hitPose) }?.let { hit -> val oldAnchor = anchorNodes[selectedNodeIndex].anchor oldAnchor?.detach() val newAnchor = hit.createAnchor() anchorNodes[selectedNodeIndex].anchor = newAnchor points[selectedNodeIndex] = anchorNodes[selectedNodeIndex].worldPosition updateMeasurements() updateVerticalLines() } } } private fun addVerticalLine(position: Vector3) { MaterialFactory.makeOpaqueWithColor(this, com.google.ar.sceneform.rendering.Color(1f, 0.8f, 0f)) .thenAccept { material -> val lineHeight = 0.5f val line = ShapeFactory.makeCube(Vector3(0.005f, lineHeight, 0.005f), Vector3.zero(), material) val lineNode = Node() lineNode.setParent(arFragment.arSceneView.scene) lineNode.worldPosition = Vector3(position.x, position.y + lineHeight/2, position.z) lineNode.renderable = line verticalLines.add(lineNode) } } private fun updateVerticalLines() { verticalLines.forEachIndexed { index, lineNode -> val position = points[index] lineNode.worldPosition = Vector3(position.x, position.y + 0.25f, position.z) } } private fun updateMeasurements() { measurementNodes.forEach { it.setParent(null) } measurementNodes.clear() points.forEachIndexed { index, currentPoint -> if (points.size > 1) { val nextPoint = points[(index + 1) % points.size] val distance = calculateDistance(currentPoint, nextPoint) val midPoint = Vector3( currentPoint.x, currentPoint.y + 0.25f, currentPoint.z ) createDistanceLabel(midPoint, distance) } } } private fun createDistanceLabel(position: Vector3, distance: Float) { ViewRenderable.builder() .setView(this, R.layout.area_label) .build() .thenAccept { renderable -> val labelNode = Node() labelNode.setParent(arFragment.arSceneView.scene) labelNode.worldPosition = position labelNode.renderable = renderable val textView = renderable.view as TextView textView.text = String.format("%.2f m", distance) measurementNodes.add(labelNode) } } private fun showAreaLabel(area: Double) { areaLabelNode?.setParent(null) val centerX = points.map { it.x }.average().toFloat() val centerY = points.map { it.y }.average().toFloat() val centerZ = points.map { it.z }.average().toFloat() ViewRenderable.builder() .setView(this, R.layout.area_label) .build() .thenAccept { renderable -> val labelNode = Node() labelNode.setParent(arFragment.arSceneView.scene) labelNode.worldPosition = Vector3(centerX, centerY + 0.3f, centerZ) labelNode.renderable = renderable val textView = renderable.view as TextView textView.text = String.format("S=%.2f m²", area) areaLabelNode = labelNode } } private fun calculateDistance(point1: Vector3, point2: Vector3): Float { val dx = point2.x - point1.x val dz = point2.z - point1.z return sqrt(dx * dx + dz * dz) } private fun calculatePolygonArea(vertices: List<Vector3>): Double { var area = 0.0 val n = vertices.size for (i in 0 until n) { val j = (i + 1) % n area += vertices[i].x * vertices[j].z area -= vertices[j].x * vertices[i].z } return abs(area) / 2.0 } private fun undoLastPoint() { if (anchorNodes.isNotEmpty()) { anchorNodes.last().let { it.anchor?.detach() it.setParent(null) } anchorNodes.removeLast() points.removeLast() if (verticalLines.isNotEmpty()) { verticalLines.last().setParent(null) verticalLines.removeLast() } updateMeasurements() if (points.size < 3) { areaLabelNode?.setParent(null) areaLabelNode = null } } } override fun onDestroy() { super.onDestroy() anchorNodes.forEach { it.setParent(null) } measurementNodes.forEach { it.setParent(null) } verticalLines.forEach { it.setParent(null) } areaLabelNode?.setParent(null) } }
After adding above code run your App and measurement area With Google AR Core. once app run showing AR Core screen to find the surface after detecting surface your can set point of your area A-B-C-D minimum 3 point you need to calculate area.