
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.





