2

I have a Database which stores a lot of markers and I am developing an application on android which includes Google Maps Android V2.

I have been searching a method to load not all markers which are stored in this database but only those are visible on the screen but nothing found, how could I do it please?

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
PHP5
  • 69
  • 2
  • 6

3 Answers3

0

I don't think there will be a way to do this in bulk, you'll have to operate on each marker one at a time. Get the bounds of the map area on screen: Android Google Maps get Boundary Co-ordinates and then use that LatLngBounds to check the LatLng of each marker to see if you want to display it or not.

nasch
  • 5,330
  • 6
  • 31
  • 52
  • Thank you it's a good idea, but if i have more than 10k entries in database , it will take so much time to be done ,won't it ? – PHP5 Mar 22 '15 at 16:12
  • Yeah it would probably take a while, you would definitely want to do it in a background thread. I don't know of any way faster than that though. – nasch Mar 22 '15 at 20:05
  • @PHP5 found any solution for this? – Navneet Krishna Mar 12 '18 at 05:33
0

There is a way to find all marker position on visible region. For that you have to loop all through your LatLng values.

for(int i=0; i < latLngListsize(); i++)
  mGoogleMap.getProjection().getVisibleRegion().latLngBounds.contains(new LatLng(latLngListsize.get(i).getLatitude(), latLngListsize.get(i).getLongitude()));

Hope this helps you!

Karolis Koncevičius
  • 9,417
  • 9
  • 56
  • 89
0

So the real issue is that you want to adjust your query, to filter out the ones you can't see on the map, so that you don't have to load them or iterate over them.

Since this doesn't have to have a lot of precision, you could store your marker points with a geohash, as well as their lat/long.

Geohash is really easy to use in a query to limit records, since it's a sequential string representation of the coordinates.

For instance, if you have two marker records with a geohash of 2j3l45d6 and 2j3lyt76, and your radius around the point is 60km, then you would set up your query something like:

SELECT * FROM points WHERE geohash LIKE "2j3%"

Geohash is not all that precise, for example, between precision 3 and 2 the visible radius jumps from 78km to 630km, but it's enough to limit the number of points you are querying, for most reasonable map camera zoom levels.

UPDATE:

I came here looking for a solution to a similar problem, and after writing this, went away and implemented it. This is a first crack, but I thought I'd share the code for anyone interested in trying it out.

There is a little extra code here, such as being able to specify the return radius in meters, which is not strictly required for this, but that I use elsewhere.

The first step was to choose a GeoHash library. Since I'm using Kotlin, I selected Android-Kotlin-Geohash, but pretty much any library will work.

Next I set up a couple of extensions for the VisibleRegion class, which is what you get back from the GoogleMap object on Android:

/**
 * Returns a geohash based on the point, with precision great enough to cover the radius.
 */
fun VisibleRegion.visibleRadiusGeohash(point: LatLng): GeoHash? {
    return GeoHash(
        lat = point.latitude,
        lon = point.longitude,
        charsCount = when (calculateVisibleRadius(UnitOfMeasure.KILOMETER)) {
            in 0.0..0.019 -> 8
            in 0.02..0.076 -> 7
            in 0.077..0.61 -> 6
            in 0.62..2.4 -> 5
            in 2.5..20.0 -> 4
            in 21.0..78.0 -> 3
            in 79.0..630.0 -> 2
            else -> 1 // 2,500km and greater
        }
    )
}

/**
 * @param units one of UnitOfMeasure.KILOMETER or UnitOfMeasure.METER.
 * Any other unit will throw an IllegalArgumentException.
 *
 * This code adapted from https://stackoverflow.com/a/49413106/300236
 */
@Throws(IllegalArgumentException::class)
fun VisibleRegion.calculateVisibleRadius(units: UnitOfMeasure): Double {
    val diameter = FloatArray(1)
    Location.distanceBetween(
        (farLeft.latitude + nearLeft.latitude) / 2,
        farLeft.longitude,
        (farRight.latitude + nearRight.latitude) / 2,
        farRight.longitude,
        diameter
    )
    // visible radius in meters is the diameter / 2, and /1000 for Km:
    return when (units) {
        UnitOfMeasure.KILOMETER -> (diameter[0] / 2 / 1000).toDouble()
        UnitOfMeasure.METER -> (diameter[0] / 2).toDouble()
        else -> throw IllegalArgumentException("Valid units are KILOMETER and METER only")
    }
}

Since I'm using MVVM, I set up a view model with a few observable properties. One is the filter I'm going to use, the other is my list of points to draw on the map. Note that null is a perfectly valid value for the filter, and will cause all markers to be loaded:

    val markerFilter: MutableLiveData<String?> = MutableLiveData<String?>(null)

    val markers: LiveData<List<Mark>> =
        Transformations.switchMap(markerFilter) { geoHash ->
            liveData(Dispatchers.IO) {
                emitSource(markRepo.loadMarkerList(geohash = geoHash))
            }
        }

If you are not familiar with ViewModel, this code essentially tells the "markers" list to update, whenever "markerFilter" changes.

Next, I need to know when the camera moved and the map viewport changes, so in my map view fragment, I listen for map movement events.

override fun onCameraMove() {
        mapView?.let { map ->
            val visibleRegion: VisibleRegion = map.projection.visibleRegion
            val centreOfVisibleRegion = visibleRegion.latLngBounds.center
            val geohash = visibleRegion.visibleRadiusGeohash(centreOfVisibleRegion)
            model. markerFilter.postValue(geohash.toString())
        }

    }

(Looking at this code, I can see a nice refactoring that will simplify this, but this will work until I go back and do it)

... and of course, I need to observe my list of markers, so I can add them to the map when the list changes:

model.markers.observe(viewLifecycleOwner, Observer { list ->
                list?.let {addMarkersToMap(it)}
})

The final bit is to set up the Repository to run the query, so my MarkerRepository class looks like this:

suspend fun loadMarkerList(geohash: String?): LiveData<List<Mark>> {
        return withContext(io) {
            dao.loadMarkerList(geohash ?: "")
        }
    }

Note that I don't pass a null filter value, if it's null, I set it to an empty string, so the resulting LIKE criteria will function.

... and my MarkerDao looks like:

@Query("SELECT * FROM marker WHERE geohash LIKE  :filter || '%' ORDER BY geohash")
    fun loadMarkerList(filter: String): LiveData<List<Mark>>

Hopefully, this gives someone else enough of a solution, to try it themselves.

Brill Pappin
  • 4,692
  • 1
  • 36
  • 36