kotlin google maps running custom Coroutines - android

How to run a custom Coroutine when using google maps. I have a function that returns the current location but I want to call this function without having to press a button or move the map. In summary I'm trying to create a custom Coroutine to call getMyCurrentLocation() every 500ms to 1000ms, exact timing is not so important. Should be similar to setOnCameraMoveListener() just run without having to have a user triggered event. I have tried examples for custom threads and coroutines but I don't get the data that I get from calling setOnCameraMoveListener(). Brand new to kotlin.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMapsBinding.inflate(layoutInflater)
setContentView(binding.root)
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
getMyCurrentLocation()
}
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
var longLatHolder112 = LatLng(0.0,0.0)
mMap.setMapStyle(MapStyleOptions.loadRawResourceStyle(this, R.raw.style_json))
val locationMyHome = LatLng(22.869559, 10.915800)
mMap.moveCamera(CameraUpdateFactory.newLatLng(locationMyHome))
mMap.setMinZoomPreference(16.0f)
longLatHolder112 = getMyCurrentLocation()
val blueDot = BitmapDescriptorFactory.fromResource(R.drawable.bluemarker)
val mo = MarkerOptions()
.position(longLatHolder112)
.title("Current Location Marker")
.snippet("me")
.icon(blueDot)
.anchor(0.5f, 0.5f)
val myMarker : Marker = mMap.addMarker(mo)
myMarker.setPosition(longLatHolder112)
mMap.setOnCameraMoveListener {
longLatHolder112 = getMyCurrentLocation()
myMarker.setPosition(longLatHolder112)
}
mMap.setOnCameraMoveCanceledListener {
}
mMap.setOnMapClickListener {
longLatHolder112 = getMyCurrentLocation()
myMarker.setPosition(longLatHolder112)
}
}

Related

Osmdroid (open street map) marker slow to draw after click to map

I'm brand new to Open Street Map and have successfully added a map to my activity at the user's location and implemented an onClickListener that upon a click shows a marker and retrieves the latitude and longitude of that point. Here is my code
private fun showMap(dLatInit: Double, dLonInit: Double){
mapView.setTileSource(TileSourceFactory.MAPNIK)
Configuration.getInstance().userAgentValue =packageName
mapView.setMultiTouchControls(true)
mapController = mapView.controller as MapController
mapController.setZoom(10.0)
val pointCenter= GeoPoint(dLatInit, dLonInit)
mapController.setCenter(pointCenter)
val mReceive: MapEventsReceiver = object : MapEventsReceiver {
override fun singleTapConfirmedHelper(geoPoint: GeoPoint): Boolean {
Marker(mapView).apply {
this.position = geoPoint
this.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
mapView.overlays.add(this)
}
populateEditText(textInputLayoutMapLat.editText, geoPoint.latitude.also { dMapLat =it })
populateEditText(textInputLayoutMapLon.editText, geoPoint.longitude.also { dMapLon =it })
return false
}
override fun longPressHelper(p: GeoPoint): Boolean {
return false
}
}
val overlayEvents = MapEventsOverlay(mReceive)
mapView.overlays.add(overlayEvents)
}
It works. The latitude and longitude immediately populate, however the marker takes from 3-5 seconds to show on my map. I'd like the marker to show much quicker.
My dependency
implementation 'org.osmdroid:osmdroid-android:6.0.3'
add
mapView.postInvalidate()
to force a redraw

How to generate marker array in Kotlin

I have 2 separate location data and I need to make array of them (I guess I need to make array of them, maybe you have better idea!) in order to add marker for both locations in google map.
Code
Locations part are commented
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
// Try to obtain the map from the SupportMapFragment.
if (ContextCompat.checkSelfPermission(
requireContext(),
android.Manifest.permission.ACCESS_FINE_LOCATION
) ==
PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(
requireContext(),
android.Manifest.permission.ACCESS_COARSE_LOCATION
) ==
PackageManager.PERMISSION_GRANTED) {
googleMap.setMyLocationEnabled(true);
googleMap.getUiSettings().setMyLocationButtonEnabled(true);
} else {
Toast.makeText(context, "Allow location access", Toast.LENGTH_LONG).show();
}
mFusedLocationClient = context?.let { LocationServices.getFusedLocationProviderClient(it) }!!
mFusedLocationClient.lastLocation
.addOnSuccessListener { location: Location? ->
if (location != null) {
// Location 1 (current location of user)
val driverLatLng = LatLng(location.latitude, location.longitude)
mMap!!.moveCamera(CameraUpdateFactory.newLatLngZoom(driverLatLng, 15f))
// Zoom in, animating the camera.
mMap!!.animateCamera(CameraUpdateFactory.zoomIn())
}
}
//new
// Location 2
val geoCoder = Geocoder(context)
var address = geoCoder.getFromLocationName(customerAddressArgument, 1)!![0]
val customerLatLng= LatLng(address.latitude, address.longitude)
mMap!!.addMarker(MarkerOptions().position(customerLatLng).title("Customer Location"))
val cameraPosition = CameraPosition.Builder()
.target(customerLatLng) // Sets the center of the map to Mountain View
.zoom(17f) // Sets the zoom
.bearing(90f) // Sets the orientation of the camera to east
.tilt(30f) // Sets the tilt of the camera to 30 degrees
.build() // Creates a CameraPosition from the builder
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
}
What I need is to make array of: val driverLatLng = LatLng(location.latitude, location.longitude) and val customerLatLng = LatLng(address.latitude, address.longitude)
Any idea?
Here is the solution for adding marker to map
first get both location point like location2 and location2
val markerOptions = MarkerOptions()
// First marker
markerOptions.position(location1).icon(BitmapDescriptorFactory.fromBitmap
(BitmapFactory.decodeResource(resources, R.mipmap.ic_user_location)))
mMap.addMarker(markerOptions)
// second marker
markerOptions.position(location2).icon(BitmapDescriptorFactory.fromBitmap
(BitmapFactory.decodeResource(resources, R.mipmap.ic_user_location)))
mMap.addMarker(markerOptions)
// Move camera
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
// resize icon
private fun resizeIcon():Bitmap{
val height = 120
val width = 60
val b: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_map_marker)
val smallMarker = Bitmap.createScaledBitmap(b, width, height, false)
return smallMarker
}
Create
var markerList:ArrayList<Marker> = ArrayList()
Create your marker
val marker = MarkerOptions().position(latlng).anchor(
0.5f,
0.5f
).title(your tittle).snippet(it.event_id).icon(your marker icon)
//Than add markers into marker list
markerList.add(marker)
use for loop to set the marker into google map
markerList.forEach{
// set marker to your google map with it
}

Creating dinamicaly Markers and accessing by variables for Google Maps Api on Kotlin

Im making a project where i need to add and remove some markers created dynamically. i'm creating my markes this way:
private fun AddMarkerFromArray(name:String, adrs:String,cod:String,imgSrc:String){
val info = MarkerDataNormal(name, adrs,
cod )
val markerOptions = MarkerOptions()
markerOptions.position(LatLng(0.0,0.0))
.title("Location Details")
.snippet("I am custom Location Marker.")
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE)
)
val customInfoWindow = CustomInfoWindowGoogleMap(this)
mMap.setInfoWindowAdapter(customInfoWindow)
val spot: Marker = mMap.addMarker(markerOptions)
spot.tag = info
// spot.showInfoWindow()
}
And this is the rest of code:
data class MarkerDataNormal(val mLocatioName: String,
val mLocationAddres: String,
val mLocationCood: String)
class CustomInfoWindowGoogleMap(val context: Context) : GoogleMap.InfoWindowAdapter {
override fun getInfoContents(p0: Marker?): View {
var mInfoView = (context as Activity).layoutInflater.inflate(R.layout.markernormal, null)
var mInfoWindow: MarkerDataNormal? = p0?.tag as MarkerDataNormal?
mInfoView.txtNombre.text = mInfoWindow?.mLocatioName
mInfoView.txtDireccion.text = mInfoWindow?.mLocationAddres
mInfoView.txtCoordenadas.text = mInfoWindow?.mLocationCood
return mInfoView
}
override fun getInfoWindow(p0: Marker): View? {
return null
}
}
so i'm using the function to make apear all the Markers i need, they are displayed correctly, but i can't clear() only one cause every one of them are "spot"
how can i change the name of the variable or assign an id to each one for later access?
I think you should store the references to the marker objects in a List<Marker>. This way you will have access to these references later.

Is there a way to set the direction of google maps according to the accelerometer?

I would like to know if there is a way of using the accelerometer of an Android device in order to set the direction of the displayed Google Maps ?
Here is how I get a reference to the Accelerometer :
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensorAccelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
if (sensorAccelerometer != null) sensorManager.registerListener(this, sensorAccelerometer, SensorManager.SENSOR_DELAY_NORMAL)
override fun onSensorChanged(event: SensorEvent?) {
if (event?.sensor?.type == Sensor.TYPE_ACCELEROMETER){
Log.d(TAG, "onSensorChanger ACCELEROMETER")
val accelX = event.values[0]
val accelY = event.values[1]
val accelZ = event.values[2]
textViewAdView.text = "aX=${accelX.toString()}\r\naY=${accelY.toString()}\r\naZ=${accelZ.toString()}"
}
}
Thanks !
You can just show the user's current location and direction of movement using googleMap.setMyLocationEnabled(true);
But if you want to turn the whole map, you can use updateCameraBearing():
https://stackoverflow.com/a/37486292/7434090
I already use this feature with the following Kotlin code :
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
mMap.uiSettings.isCompassEnabled = true
mMap.uiSettings.isMapToolbarEnabled = true
mMap.uiSettings.isMyLocationButtonEnabled = true
try {
mMap.isMyLocationEnabled = true
}catch (e: SecurityException) {
}
}
I'll check the bearing stuff, thanks
I will try this when outside :
When getting a new location from a fusedLocationClient :
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
...
updateCamera(location.bearing)
....
private fun updateCamera(bearing : Float) {
val oldPos = mMap.getCameraPosition()
val pos = CameraPosition.builder(oldPos).bearing(bearing).build()
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(pos))
textViewAdView.text = "Bearing=$bearing"
}

Change cluster manager's item icon programmatically

On my map I use the Marker Cluster Utility to group the markers. All the markers when first put on the map have the same icon, then, when I move close to one of the markers, its icon must change. I've read other discussions about this, but as far as I've understood, I'd need to remove the marker and generate it again with the new icon.
My markers belong to a cluster, so I should remove the marker from the cluster, generate a new marker and add it to the cluster manager object.
The problem is that the cluster manager object has a renderer attached to it which also defines the marker's icon and it would use the same icon as for the removed marker.
Some code:
the renderer class
class VenueMarkerRender(private val context: Context, map: GoogleMap, clusterManager: ClusterManager<Venue>)
: DefaultClusterRenderer<Venue>(context, map, clusterManager) {
override fun onBeforeClusterItemRendered(item: Venue?, markerOptions: MarkerOptions?) {
super.onBeforeClusterItemRendered(item, markerOptions)
markerOptions!!.icon(bitmapDescriptorFromVector(context, R.drawable.ic_map_black))
}
override fun onBeforeClusterRendered(cluster: Cluster<Venue>?, markerOptions: MarkerOptions?) {
super.onBeforeClusterRendered(cluster, markerOptions)
markerOptions!!.icon(bitmapDescriptorFromVector(context, R.drawable.ic_home_black_24dp))
}
override fun shouldRenderAsCluster(cluster: Cluster<Venue>?): Boolean {
return cluster!!.size > 1
}
/**
* Takes a vector image and make it available to use as a marker's icon
*/
private fun bitmapDescriptorFromVector(context: Context, #DrawableRes vectorDrawableResourceId: Int): BitmapDescriptor {
// ...
return BitmapDescriptorFactory.fromBitmap(bitmap)
}
}
the Venue class
class Venue : ClusterItem {
private var mPosition: LatLng
private var mTitle: String? = null
private var mSnippet: String? = null
constructor(lat: Double, lng: Double, title: String, snippet: String) {
mPosition = LatLng(lat, lng)
mTitle = title
mSnippet = snippet
}
override fun getPosition(): LatLng {
return mPosition
}
override fun getTitle(): String {
return mTitle!!
}
override fun getSnippet(): String? {
return mSnippet
}
}
finally how the cluster manager is created and how a venue is added to it
mClusterManager = ClusterManager(this, map)
val renderer = VenueMarkerRender(this, map, mClusterManager!!)
mClusterManager!!.renderer = renderer
// other code
for (i in 0 until markers.length()) {
val marker = JSONObject(markers.getJSONObject(i).toString())
val venue = Venue(
marker.getDouble("lat"),
marker.getDouble("lng"),
marker.getString("title"),
marker.getString("snippet"),
)
mClusterManager!!.addItem(venue)
}
mClusterManager!!.cluster()
Is it possible to generate a new Venue object with its own icon and to add it to the cluster manager object? Or is there a better way to obtain what I need?
I've just found the solution, I hope this will help someone else.
I've declared the renderer as a class attribute to make it available everywhere inside the activity
private var renderer: VenueMarkerRender? = null
before it was a private variable inside the method which sets up the Cluster Manager. Then it is initialized as already shown in the previous message
renderer = VenueMarkerRender(this, map, mClusterManager!!)
Now to change the marker when I get close to it, it is enough to call this method each time that the location changes
private fun markerProximity() {
// get the venues' list from the cluster
val venues = mClusterManager!!.algorithm.items
// if the cluster was not empty
if (venues.isNotEmpty()) {
// initialize the array which will contain the distance
val distance: FloatArray = floatArrayOf(0f,0f,0f)
// loop through all the venues
for (venue:Venue in venues) {
// get the distance in meters between the current position and the venue location
Location.distanceBetween(
venue.position.latitude,
venue.position.longitude,
lastLocation.latitude,
lastLocation.longitude,
distance)
// if closer than 3 meters
if ( distance[0] < 3 ) {
// change this marker's icon
renderer!!.getMarker(venue)
.setIcon(BitmapDescriptorFactory
.fromResource(R.drawable.my_location))
}
}
}
}

Categories

Resources