Android Kotlin

Area measurement app in Android With Google AR Core

Area measurement app in Android With Google AR Core
79views

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.

 

 

Welcome to my blog! I’m Ritu Malik, and here at Codeplayon.com, we are dedicated to delivering timely and well-researched content. Our passion for knowledge shines through in the diverse range of topics we cover. Over the years, we have explored various niches such as business, finance, technology, marketing, lifestyle, website reviews and many others.