6

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)

enter image description here

AVEbrahimi
  • 17,993
  • 23
  • 107
  • 210
  • 1
    It looks like this feature was requested in Google issue tracker: https://issuetracker.google.com/issues/35823783#comment13 – xomena Apr 17 '18 at 10:47

3 Answers3

0

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.

Prashant Abdare
  • 2,175
  • 14
  • 24
0

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
    }
}
jc12
  • 1,411
  • 1
  • 18
  • 24
0

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.

Evan Bain
  • 59
  • 4