I've got the following Unit function:
private fun loadFilterArea() {
val query = ALIAS.AREA
val type = preferences.getInt(query, TYPEVALUES.MY_LOCATION)
val longitude: Double
val latitude: Double
when (type) {
TYPEVALUES.MY_LOCATION -> {
...
longitude = point.longitude
latitude = point.latitude
...
}
TYPEVALUES.CUSTOM_RADIUS -> {
...
longitude = point.longitude
latitude = point.latitude
...
}
TYPEVALUES.INPUT -> {
...
longitude = preferences.getFloat(ALIAS.AREA_LONGITUDE, 0f).toDouble()
latitude = preferences.getFloat(ALIAS.AREA_LATITUDE, 0f) .toDouble()
...
} else -> {
...
}
}
//... the question is here ...
//... can't be compiled cause val's are not initialized
if (longitude != null && latitude != null) {
storeKey(ALIAS.AREA_LONGITUDE, longitude)
storeKey(ALIAS.AREA_LATITUDE, latitude)
}
}
So, how can check if values longitude and latitude are initialized ?
I don't want to move them from function to global scope and use them as lateinit with further ::latitude.isInitialized, and it seems like my version of Kotlin doesn't support local lateinit variables
If it makes sense to have null values for latitude and longitude for your use case, then you can define those variables as nullable Double, e.g., var latitude: Double? = null (the same for longitude). After that, it makes sense to check if they're not null, but the variable becomes no more immutable.
In order to keep immutability, you can do something like that:
val longitude: Double?
val latitude: Double?
when (type) {
TYPEVALUES.MY_LOCATION -> {
longitude = ...
latitude = ...
...
}
TYPEVALUES.CUSTOM_RADIUS -> {
longitude = ...
latitude = ...
...
}
TYPEVALUES.INPUT -> {
...
} else -> {
latitude = null
longitude = null
}
}
Otherwise, if you want them to be non-nullable, then you need to provide a meaningful, non-null value in all branches of the when statement.
You could use the power of when to store the value directly, so that declaration and assignments are done at the same time:
val (latitude, longitude) = when (type) {
TYPEVALUES.MY_LOCATION -> Pair(0.0, 0.0)
TYPEVALUES.CUSTOM_RADIUS -> Pair(1.0, 1.0)
else -> Pair(2d, 2d)
}
//Your latitude is a double
//Your longitude is a double
Related
I have the following problem.
I receive in my LocationChangeListeningCallback latitude and longitude but getting the speed is not possible. It is always 0.
Here is my code (I hope I have not omitted anything).
AndroidManidest.xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
build.gradle
//MapBox
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:8.6.2'
implementation('com.google.android.gms:play-services-location:18.0.0')
MainActivity.kt
override fun onMapReady(mapboxMap: MapboxMap) {
map = mapboxMap
callback = LocationChangeListeningCallback()
mapboxMap.setStyle(Style.MAPBOX_STREETS) {
enableLocationComponent(it)
}
}
private inner class LocationChangeListeningCallback : LocationEngineCallback {
override fun onSuccess(result: LocationEngineResult?) {
result?.lastLocation ?: return
if (result.lastLocation != null){
val lat = result.lastLocation?.latitude!!
val lng = result.lastLocation?.longitude!!
val latLng = LatLng(lat, lng)
if (result.lastLocation != null) {
map.locationComponent.forceLocationUpdate(result.lastLocation)
val position = CameraPosition.Builder()
.target(latLng)
.zoom(13.0)
.tilt(10.0)
.build()
map.animateCamera(CameraUpdateFactory.newCameraPosition(position))
point = Point.fromLngLat(lng, lat)
pointList.add(point)
if(pointList.size>1) {
var location = Location(LocationManager.GPS_PROVIDER)
val results = FloatArray(5)
val lastlat = pointList[pointList.lastIndex - 1]
var xLng = lastlat.coordinates()[0]
var yLat = lastlat.coordinates()[1]
Location.distanceBetween(yLat, xLng, lat, lng, results)
//--> Here speed is always 0.0
if(location.hasSpeed())
speed = location.speed/18*5
else
speed = 0F
accDistance += results[0]
textViewDistance.text = "Distance in meters covered: " + accDistance + "\n" + "Current Velocity: " + speed
} else {
textViewDistance.text = "Distance in meters covered: 0.0" + "\n" + "Current Velocity: 0"
}
map_view.getMapAsync(OnMapReadyCallback { mapboxMap ->
mapboxMap.setStyle(Style.MAPBOX_STREETS) { style ->
// Create the LineString from the list of coordinates and then make a GeoJSON
// FeatureCollection so we can add the line to our map as a layer
style.addSource(
GeoJsonSource(
"line-source",
FeatureCollection.fromFeatures(
arrayOf(
Feature.fromGeometry(
LineString.fromLngLats(pointList)
)
)
)
)
)
// The layer properties for our line
style.addLayer(
LineLayer("linelayer", "line-source").withProperties(
PropertyFactory.lineWidth(3f),
PropertyFactory.lineColor(Color.parseColor("#FF0000"))
)
)
}
})
}
}
}
override fun onFailure(exception: Exception) {}
}
I hope somebody can help me.
I have not found anything on the Internet to solve my problem.
I'm working on my study android project where I need to place 5 random markers within a 10km radius of my geolocation. Here's my function to generate random coordinates:
fun generateRandomCoordinates(min: Int, max: Int): LatLng {
val coordinates: LatLng
val currentLong: Double
val currentLat: Double
val meterCord = 0.00900900900901 / 1000
//Generate random Meters between the maximum and minimum Meters
val r = Random()
val randomMeters: Int = r.nextInt(max + min)
//then Generating Random numbers for different Methods
val randomPM: Int = r.nextInt(6)
//Then we convert the distance in meters to coordinates by Multiplying number of meters with 1 Meter Coordinate
val metersCordN = meterCord * randomMeters.toDouble()
val locationResult = fusedLocationProviderClient.lastLocation
currentLong = locationResult.result.longitude
currentLat= locationResult.result.latitude
coordinates = when (randomPM) {
0 -> LatLng(currentLat + metersCordN, currentLong + metersCordN)
1 -> LatLng(currentLat - metersCordN, currentLong - metersCordN)
2 -> LatLng(currentLat + metersCordN, currentLong - metersCordN)
3 -> LatLng(currentLat - metersCordN, currentLong + metersCordN)
4 -> LatLng(currentLat, currentLong - metersCordN)
else -> LatLng(currentLat - metersCordN, currentLong)
}
return coordinates
}
that code results with java.lang.IllegalStateException: Task is not yet complete at line currentLong = locationResult.result.longitude
I tried to use addOnCompleteListener but somehow it didn't worked out. So I tried to add Thread.sleep(50) and that works but I think that it's not how sane person should code. How can I solve this problem?
as It can take some time to update the position, you should register a callback for retrieving the current location. Note that this code only returns the last known location, if you want to get updated location continuously, you can use the requestLocationUpdates method.
fusedLocationClient.getLastLocation()
.addOnSuccessListener(this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
// Got last known location. In some rare situations this can be null.
if (location != null) {
// Logic to handle location object
}
}
});
Turn on your location. 2. check whether location is null or not:
fun generateRandomCoordinates(min: Int, max: Int): LatLng {
val coordinates: LatLng
val currentLong: Double
val currentLat: Double
val meterCord = 0.00900900900901 / 1000
//Generate random Meters between the maximum and minimum Meters
val r = Random()
val randomMeters: Int = r.nextInt(max + min)
//then Generating Random numbers for different Methods
val randomPM: Int = r.nextInt(6)
//Then we convert the distance in meters to coordinates by Multiplying
number of meters with 1 Meter Coordinate
val metersCordN = meterCord * randomMeters.toDouble()
val locationResult = fusedLocationProviderClient.lastLocation
if (location != null) { //<------
currentLong = locationResult.result.longitude
currentLat= locationResult.result.latitude
coordinates = when (randomPM) {
0 -> LatLng(currentLat + metersCordN, currentLong + metersCordN)
1 -> LatLng(currentLat - metersCordN, currentLong - metersCordN)
2 -> LatLng(currentLat + metersCordN, currentLong - metersCordN)
3 -> LatLng(currentLat - metersCordN, currentLong + metersCordN)
4 -> LatLng(currentLat, currentLong - metersCordN)
else -> LatLng(currentLat - metersCordN, currentLong)
}
return coordinates
} else { //<------
Toast.makeText(getActivity(), "Null location!",
Toast.LENGTH_LONG).show();
}
}
If the above code returned else part, then most likely your last location is empty (if you are using emulator!) In emulator like BlueStacks Ctrl+Shift +K (for other emulators see this post: How to emulate GPS location in the Android Emulator?) and set a location, then try your code again.
how can I get the Distance out of Mapbox Navigation Route? in Double Format, I don't want to draw the route I just need to calculate the distance between two Points, I have been able to calculate the distance using Truf
Storelocation = Point.fromLngLat(Stores.latitude, Stores.longitude)
Userlocation = Point.fromLngLat(UserLocation.Latitude, UserLocation.Longitude)
Distance = TurfMeasurement.distance(Storelocation, Userlocation, TurfConstants.UNIT_KILOMETERS)
but the problem is with this method above it doesn't calculate distance within the route, it is just calculate straight line from point to point for example in Google map the distance is 9 Km, but with this method above the distance is 6 Km
private fun getRout(){
NavigationRoute.builder(this)
.accessToken(Mapbox.getAccessToken()!!)
.origin(Userlocation)
.destination(Storelocation).build().getRoute(object :Callback<DirectionsResponse> {
override fun onResponse(call: Call<DirectionsResponse>, response: Response<DirectionsResponse>) {
val rout = response ?: return
val body = rout.body() ?: return
if (body.routes().count() == 0 ){
return
}
navigationMapRoute = NavigationMapRoute(null, mapView, map)
// Get Distance
Distance = ??
}
override fun onFailure(call: Call<DirectionsResponse>, t: Throwable) {
}
})
}
Instead of this:
navigationMapRoute = NavigationMapRoute(null, mapView, map)
// Get Distance
Distance = ??
Do this:
val route = response.body().routes().get(0)
val distance = route.distance()
When i start my app, which should get the speed of the device, i get a NaN( i think its for " not a number"?) value for 1- 3 seconds in the textfield.The numbers are safed in a repository, but if is Nan, the app crashes.
How can i replace this NaN with 0(Zero)?... for ex.: If( NaNvalue = true){ (value = 0)}...something like that. I didnt find anything written in android or kotlin, just python(which i am not familiar with).
Any help would be great!!! Thank you...
my code:
fun getSpeed(location: Location): Float{
if (mRunning ) {
// val getkmh = (location.speed * 3.6)
// mSpeedKmH = getkmh.toFloat()
mElapsedSeconds = ((SystemClock.elapsedRealtime()
-mChrono!!.getBase()) / 1000)
mSpeedMeter = mDistanceTotal/mElapsedSeconds.toFloat()
mSpeed = ( mSpeedMeter *3.6).toFloat()// *3.6 > KmH
}
return mSpeedKmH
}
fun updateSpeed() {
val speedMeter = String.format(Locale.getDefault(), "%.1f",mSpeed)
tvSpeed.text = speedMeter
}
You can do it like the other answer shows or using takeIf and the elvis operator:
val speedMeter = String.format(Locale.getDefault(), "%.1f", mSpeed.takeIf { !it.isNaN() } ?: 0.0)
val value: Double = yourDoubleValue.let { if (it.isNaN()) 0.0 else it}
The same can be applied to Float values
Thanks to Fernando and Tenfour04!!! Both versions are working!!
val speedGesammt = String.format(Locale.getDefault(), "%.1f",
mSpeedAverageKmH.takeIf { !it.isNaN() } ?: 0.0)
val speedGesammt = String.format(Locale.getDefault(), "%.1f",
mSpeedAverageKmH.let { if (it.isNaN()) 0.0 else it})
I am building some app like image below, I want to force markers not to be clickable, but there is no setClickable(false) for Marker or MarkerOptions.
Currently area around marker (see attachment) is not clickable ( click is passed to marker, not map)
You have to use Overlay instead of marker in the Map to get exactly what you desire. You could follow this link, similar is done in JavaScript here.
I found a way to manually handle clicks for markers.
Add a touchable wrapper as described in this stackoverflow answer: https://stackoverflow.com/a/58039285/1499750
Add a gesture detector to your fragment and listen to single taps, then find the closest marker based on lat lng:
private var gestureDetector: GestureDetector? = null
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
gestureDetector = GestureDetector(context, GoogleMapsGestureListener { e -> onMapSingleTap(e) })
//id of touchable wrapper - can use findViewById here instead if not using kotlin synthetics
googleMapsTouchableWrapper?.onTouch = {
gestureDetector?.onTouchEvent(it)
}
}
private fun onMapSingleTap(e: MotionEvent) {
val latLng = map?.projection?.fromScreenLocation(Point(e.x.toInt(), e.y.toInt())) ?: return
//this assumes you are maintaining a set of the latlngs for your markers
val closestNearbyLatLng = markerLatLngs?.findClosestNearbyLatLng(latLng)
//assuming you have a map of latlng to marker you can now find that marker based on latlng and do whatever you need to with it
}
private fun Set<LatLng>.findClosestNearbyLatLng(latLng: LatLng): LatLng? {
val map = map ?: return null
val screenDistance = map.projection.visibleRegion.latLngBounds.northeast.distanceBetweenInKm(map.projection.visibleRegion.latLngBounds.southwest)
val closestLatLng = this.minBy { latLng.distanceBetweenInKm(it) } ?: return null
if (latLng.distanceBetweenInKm(closestLatLng) < screenDistance/40) {
return closestLatLng
}
return null
}
fun LatLong.distanceBetweenInKm(latLng: LatLng): Double {
if (this == latLng) {
return 0.0
}
val earthRadius = 6371.0 //km value;
//converting to radians
val latPoint1Radians = Math.toRadians(latitude)
val lngPoint1Radians = Math.toRadians(longitude)
val latPoint2Radians = Math.toRadians(latLng.latitude)
val lngPoint2Radians = Math.toRadians(latLng.longitude)
var distance = sin((latPoint2Radians - latPoint1Radians) / 2.0).pow(2.0) + (cos(latPoint1Radians) * cos(latPoint2Radians)
* sin((lngPoint2Radians - lngPoint1Radians) / 2.0).pow(2.0))
distance = 2.0 * earthRadius * asin(sqrt(distance))
return abs(distance) //km value
}
class GoogleMapsGestureListener(private val onSingleTap: (MotionEvent) -> Unit) : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
super.onSingleTapConfirmed(e)
e?.let { onSingleTap(it) }
return true
}
}
I recently was able to create a formula to create an area surrounding a certain position on a Google Map, that is also scalable with zoom level.
Here I converted the LatLng coordinates from the marker to actual coordinates on the phone:
//array that holds all locations of every marker
//after a marker is created add the position in here
val positionList = mutableListOf<LatLng>()
//map is variable type GoogleMap
map.setOnMapClickListener {
var inRange = false
for(i in positionList.indices) {
//establish corners of boundaries surrounding markers
val points = positionList.toCoordinates(map)
//check if clicked position falls in one of the positions' bounds
val isInRangeLng = (points[i][2]..points[i][3]).contains(it.longitude)
val isInRangeLat = (points[i][0]..points[i][1]).contains(it.latitude)
//if click lands in of the positions' bounds, stop loop and return inRange
//true
if(isInRangeLat && isInRangeLng) {
inRange = true
break
}
}
if(!inRange) {
//APPLY YOUR LOGIC IF CLICK WAS NOT IN AREA
} else {
//APPLY YOUR LOGIC IF CLICK WAS IN AREA
}
}
//Extension function used to simplify logic
/** Convert LatLng to coordinates on phone **/
fun List<LatLng>.toCoordinates(map: GoogleMap): List<List<Double>> {
val proj: Projection = map.projection
val coordinateList = mutableListOf<List<Double>>()
//create bounds for each position in list
this.forEach {
//get screen coordinates at the current LatLng
val point = proj.toScreenLocation(it)
val left = point.x - 100
val right = point.x + 100
val top = point.y - 100
val bottom = point.y + 100
//convert bounds into two points diagonal of each other
val topRight = Point(right, top)
val bottomLeft = Point(left, bottom)
//convert the two points into LatLng points and get the bounds in north,
//south, west, and east
val northEast = proj.fromScreenLocation(topRight)
val north = northEast.latitude
val east = northEast.longitude
val southWest = proj.fromScreenLocation(bottomLeft)
val south = southWest.latitude
val west = southWest.longitude
//add the bounds to be returned in a list which corresponds to a certain
//position
coordinateList.add(listOf(
south,
north,
west,
east
))
}
return coordinateList
}
This can be used for a lot more than markers too.