EricNeid / arcore-location-extension

This library provides utilities for ARCore to place ar markers on geo locations.
MIT License
14 stars 1 forks source link

Provide example #2

Closed kustraslawomir closed 4 years ago

kustraslawomir commented 4 years ago

Hi mate, will You maybe provide some examples for Your extensions? I can't get It to work :( Thank You!

kustraslawomir commented 4 years ago
      locationArMarkers.add(ArCircleExtenstionsMArker(view = witoldMarker.get(),
                                title = "Ex Witolda Park | SIMPLE STATIC",
                                marker = LocationArMarker(GlobalPosition(
                                        50.028714,
                                        22.041025),
                                        getNode(witoldaExtionsMarker.get())).apply {
                                    placementType = LocationArMarker.PlacementType.STATIC
                                }))
sceneView.scene.addOnUpdateListener {
            if (!::locationArScene.isInitialized){
                locationArScene = LocationArScene(sceneView)
                locationArScene.startLocationScene()

                locationArMarkers.forEach { marker ->
                    locationArScene.addMarker(marker.marker)
                }
            }
        }
EricNeid commented 4 years ago

Sure, i will try to create a basic example and update the readme.

EricNeid commented 4 years ago

I have added a more complete example to the readme. Please check it out and let me know if you are missing anything or if there is anything else i can help you with.

Your snippets are looking good for me. However I'm not seeing the part where to update the location and bearing. You need to manually set the current position and bearing, because my scene will not obtain a position itself.

kustraslawomir commented 4 years ago

import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.google.android.gms.location.LocationRequest
import com.google.ar.sceneform.Node
import com.google.ar.sceneform.rendering.Renderable
import com.google.ar.sceneform.rendering.ViewRenderable
import com.ultron.ar.arlocation.utils.ARLocationPermissionHelper
import com.ultron.ar.presentation.R
import com.ultron.ar.presentation.arlocation.ArLocationActivity
import com.ultron.ar.presentation.arlocation.extensions.ArSceneLifecycleUtil
import com.ultron.ar.presentation.arlocation.extensions.GlobalPosition
import com.ultron.ar.presentation.arlocation.extensions.LocationArMarker
import com.ultron.ar.presentation.arlocation.extensions.LocationArScene
import com.ultron.ar.presentation.arlocation.location.FASTEST_INTERVAL
import com.ultron.ar.presentation.arlocation.location.UPDATE_INTERVAL
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.fragment_ultron_ar.*
import org.neidhardt.rxlocation.services.BearingSensorService
import org.neidhardt.rxlocation.services.GoogleLocationService
import utils.TimberLog

open class ExtensionsArFragment : Fragment() {

    private lateinit var arLifeCycle: ArSceneLifecycleUtil

    private var bearingSubscription: Disposable? = null
    private var locationSubscription: Disposable? = null

    private lateinit var locationArScene: LocationArScene

    private lateinit var activity: ArLocationActivity

    private val bearingService: BearingSensorService by lazy {
        BearingSensorService(activity.applicationContext)
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        this.activity = context as ArLocationActivity
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = LayoutInflater
            .from(context)
            .inflate(R.layout.fragment_ultron_ar, container, false)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        ARLocationPermissionHelper.requestPermission(activity)

        locationArScene = LocationArScene(sceneView).apply {
            startLocationScene()
        }

        arLifeCycle = ArSceneLifecycleUtil(activity, sceneView)

        loadViewRenderable(
                activity,
                R.layout.view_circle_white_marker,
                { renderable ->
                    locationArScene.renderArObject(
                            50.027562,
                            22.045414,
                            -1.5f,
                            renderable)
                },
                {
                    TimberLog.e("Can't get renerable. %s", it.message)
                })

        bearingSubscription = bearingService.getBearingUpdatesFromRotation()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({ bearing ->
                    this.locationArScene.onBearingChanged(bearing.azimuth)
                }, { error ->
                    TimberLog.e("bearingSubscription error: %s", error.message)
                })

        val locationRequest = LocationRequest().apply {
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
            interval = UPDATE_INTERVAL
            fastestInterval = FASTEST_INTERVAL
        }

        val locationServiceGoogle: GoogleLocationService by lazy { GoogleLocationService(activity.applicationContext) }
        locationSubscription = locationServiceGoogle.getLocationUpdates(locationRequest)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({ location ->
                    TimberLog.d("Location: %s,%s", location.latitude, location.longitude)
                    this.locationArScene.onLocationChanged(GlobalPosition(location.latitude, location.longitude))
                }, { error ->
                    TimberLog.e("location error: %s", error.message)
                })
    }

    override fun onResume() {
        super.onResume()
        arLifeCycle.onActivityResume()
    }

    override fun onPause() {
        super.onPause()
        arLifeCycle.onActivityPause()
    }

    override fun onDestroy() {
        super.onDestroy()
        arLifeCycle.onActivityDestroy()
        bearingSubscription?.dispose()
        locationSubscription?.dispose()
    }

    private fun loadViewRenderable(
            context: Context,
            viewId: Int,
            onSuccess: (ViewRenderable) -> Unit,
            onFailure: (Throwable) -> Unit
    ) {
        ViewRenderable.builder()
                .setView(context, viewId)
                .build()
                .thenAccept(onSuccess)
                .exceptionally { error ->
                    onFailure(error)
                    null
                }
    }

    private fun LocationArScene.renderArObject(
            lat: Double,
            lng: Double,
            height: Float,
            renderable: Renderable
    ): LocationArMarker {
        val node = Node().apply {
            this.renderable = renderable
        }

        val marker = LocationArMarker(GlobalPosition(lat, lng), node).apply {
            this.height = height
            placementType = LocationArMarker.PlacementType.STATIC
            onlyRenderWhenWithin = Int.MAX_VALUE
        }
        addMarker(marker)
        return marker
    }

I don't want to bother You but if You could just take a look at what's wrong here... A can't see marker on my camera preview, but when I made it with just core https://github.com/appoly/ARCore-Location markers are rendered just fine with some other issues but still they are visible. Maybe it's some silly mistake here made by me?

Location and bearing is updated just fine with Your library, in logs I can see that renderedLocationMarkers have rendered marker inside. Really had no clue why it isn't working :)

kustraslawomir commented 4 years ago

My simple xml view:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:orientation="vertical"
    android:background="@drawable/white_circle"
    android:padding="20dp">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textColor="@color/textColor"
        android:textSize="@dimen/circleMarkerTitle" />

    <TextView
        android:id="@+id/distance"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textColor="@color/textColor"
        android:textSize="@dimen/circleMarkerSubtitle" />

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:rotation="180"
        android:scaleType="fitCenter"
        android:src="@drawable/ic_pointer" />

</LinearLayout>
kustraslawomir commented 4 years ago
const val UPDATE_INTERVAL: Long = 1 * 1000 
const val FASTEST_INTERVAL: Long = 300
LocationRequest.PRIORITY_HIGH_ACCURACY

Gps settings.

EricNeid commented 4 years ago

No problem. Your code looks good.

Please try setting the placementType = LocationArMarker.PlacementType.DYNAMIC For testing, you can also set scalingMode = FIXED_SIZE making your -> makes your marker easier to spot

Long explanation:

Static placement means that the marker is placed once a position and bearing is available and let the ar framework keep track of everything else. The marker position in ar coordinates is calculated by basically converting from Polar coordinates to Cartesian coordinates.

Dynamic placement is basically the same behavior that ARCore-location uses. The marker is placed in the direction of the target and some scaling is applied to emulate distance. I use dynamic placement during the navigation and static placement, when the user is in close proximity of his target and when I have an accurate position (which is more likely available after some moving with gps), because it is more stable.

I'm going to update the documentation regarding this.