I'm using the Mapbox Android SDK and want to populate the map with markers whose locations are stored in a Firebase database common to all users of my application. Each marker is stored in a unique record and each record contains the Latitude & Longitude of the marker along with a boolean value indicating if the location has been verified. I have successfully done this using the Mapbox iOS SDK using Annotations.
When the Android App is launched I create a feature list of all markers in the database using symbolOptions as shown below inside of the Firebase "childAdded" listener (other listeners are also implemented as part of the ChildEventListener):
DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference markerRef = dbRef.child("markers");
//load all markers when app starts
//child added returns all markers, then listens for new additions
//Log.i(TAG, "Database Reference: " + dbRef);
List<SymbolOptions> options = new ArrayList<> ();
markerRef.addChildEventListener ( new ChildEventListener () {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
markerRead = markerRead+1;
markerKey = dataSnapshot.getKey();
Log.i(TAG, "Adding Markers: ");
Log.i(TAG, "Marker Key: " + markerKey);
x = (Double)dataSnapshot.child ( "x" ).getValue();
y = (Double) dataSnapshot.child ( "y" ).getValue();
v = (Boolean) dataSnapshot.child ( "v" ).getValue();
Log.i(TAG, "Marker Data - x: " + x + " y: " + y + " v: " + v);
if(v == true) {
options.add(new SymbolOptions ()
.withLatLng ( new LatLng (y,x))
.withIconImage ( "verified" )
.withIconAnchor ( "bottom" )
.withDraggable ( false )
);
} else {
options.add(new SymbolOptions ()
.withLatLng ( new LatLng (y,x))
.withIconImage ( "unverified" )
.withIconAnchor ( "bottom" )
.withDraggable ( false )
);
}
if (markerRead == 1) {
symbols = symbolManager.create(options);
}
}
The above code does populate the feature list, but since I have no way of knowing when the Firebase "childAdded" function has completed the initial read of the database, the above only adds the first marker.
I have tried to find Mapbox documentation on how to add annotations analogous to the iOS SDK but have been unsuccessful. The tutorial videos have not been updated and use "addMarker" which has been deprecated.
I need to add markers to the map and I also need to assign a unique ID (the "markerKey" in the above code) to each marker so that an individual marker can be changed or deleted in the future. Also the map needs to display new markers that are added and detected by the "child Added" listener. I have not found a way to have additions to the feature list be displayed when added and the symbols created.
Once the markers are initially added to the Map, the App "Listens" to the Firebase database for the following events:
A new marker is added
An existing marker is moved to a new location
An existing marker is deleted
When a marker is moved or deleted the appropriate database listener is notified and passed the record number (key) of the marker ("markerKey" in above code) that was moved (or deleted). If the marker was moved, the record will then contain the new Latitude & Longitude information.
Thanks in advance for the help.
Related
Is it possible to get user's current Lat Long using Places API, instead of Maps API? I'm sure there's a process that involves user's current Lat Long but is it possible to get them?
Currently I'm creating activities that use Places API, and now I need user's current Lat Long, I think it's not worth to add Maps API only for that function.
If your requirement is to fetch only the Lat Long of the current location, then you don't need to use any Api. You may follow the below tutorial to implement it without using Place Api
https://www.androidhive.info/2015/02/android-location-api-using-google-play-services/
If you still want to use Place Api then you need to use Places.PlacesDetectionApi.getCurrentPlace method to create a PendingIntent that returns with a PlaceLikelihoodBuffer. First define the GoogleApiClient and use the below code to fetch the location -
PendingResult<PlaceLikelihoodBuffer> result = Places.PlaceDetectionApi.getCurrentPlace( mGoogleApiClient, null );
result.setResultCallback( new ResultCallback<PlaceLikelihoodBuffer>() {
#Override
public void onResult( PlaceLikelihoodBuffer likelyPlaces ) {
PlaceLikelihood placeLikelihood = likelyPlaces.get( 0 );
String content = "";
if( placeLikelihood != null && placeLikelihood.getPlace() != null && !TextUtils.isEmpty( placeLikelihood.getPlace().getName() ) )
content = "Most likely place: " + placeLikelihood.getPlace().getLatLng()+ "\n";
if( placeLikelihood != null )
content += "Percent change of being there: " + (int) ( placeLikelihood.getLikelihood() * 100 ) + "%";
mTextView.setText( content );
likelyPlaces.release();
}
});
For more info, refer to this article - https://code.tutsplus.com/articles/google-play-services-using-the-places-api--cms-23715
In the map below I have three markers, the red one is the users current position while the green ones are the clickable markers.
Clicking the bottom one is not a problem. But clicking the one the user is close to is a different matter as the position marker is in the way. So for that one the user usually has to click 2 - 8 times before the green one is touched.
Is there a way to define that some markers are not clickable so they dont trigger the onMarkerClickListener?
I am aware that I can change the z-index and put the position marker below but it wont be nice for the user if he can't see his own position when close to another marker.
Another solution can think of but dont like is to intercept the click for the user marker and then go through a list of the other markers and find possible matches and trigger a click myself. I'd prefer an existing solution.
So what I have ended up doing is adding the object the marker represents to its tag. Then in onMarkerClickListener I check the marker type by the tag object. Like so (kotlin):
map.setOnMarkerClickListener { marker ->
val mapEvent: MapEvent?
mapEvent =
if (marker.tag is MapEvent) {
marker.tag as MapEvent?
} else {
val clickedMarker = getClickedMarker(from = map)
if (clickedMarker != null)
clickedMarker.tag as MapEvent?
else
return#setOnMarkerClickListener false
}
markerClicked(this#MainActivity, mapEvent)
true
}
If the object is of type MapEvent I call markerClicked() if the clicked marker is not of type MapEvent I go through a list of all markers to see if the clicked marker is inside the radius of any of the MapEvent markers. If so I use that marker and call markerClicked().
This is how I sort through the list of markers:
fun getClickedMarker(from: GoogleMap): Marker? {
if (!::userMarker.isInitialized)
return null
var clickWidth = ContextCompat.getDrawable(App.context, R.drawable.ic_map_permanent)!!.intrinsicWidth
clickWidth += (clickWidth / 3)
val g0 = from.projection.fromScreenLocation(Point(0, 0))
val g1 = from.projection.fromScreenLocation(Point(clickWidth, clickWidth))
val latRadius = (g1.latitude - g0.latitude) / 2
val lonRadius = (g1.longitude - g0.longitude) / 2
return try {
eventMarkers.single {
it.position!!.latitude - latRadius > userMarker.position.latitude && // west
it.position!!.latitude + latRadius < userMarker.position.latitude && // east
it.position!!.longitude + lonRadius > userMarker.position.longitude && // north
it.position!!.longitude - lonRadius < userMarker.position.longitude // south
}
} catch (e: Exception) {
null
}
}
Please comment if anything is unclear. I hope this might help anyone else!
I am creating an Android App , an Uber Clone. In this I am using Firebase to list the rides created by the user.
The Ride stored in the following format .
{
"category" : "NUVO",
"driverids" : "",
"drop_lat" : "40.7127753",
"drop_long" : "-74.0059728",
"estdate" : "Wed#Feb#28th",
"estmile" : "4.19",
"esttime" : "01:02#PM",
"esttotalfare" : "4.61",
"fareest" : "1.1",
"g" : "t9z83wv46x",
"l" : [ 40.674426, -73.944033 ],
"notes" : "Gv",
"orgin_lat" : "145 Brooklyn Ave, Brooklyn, NY 11213, USA%20New York, NY, USA",
"request_id" : "5a75579740d166e95b8b4567",
"requested_category" : "FAM_SUV",
"rider_id" : "5a507bf340d16660288b4567",
"status" : 1,
"trip_id" : 0
}
Now I am Using GeoFire's GeoQuery's queryAtLocation to pick all rides with in 5 KMs from his current location.
The query uses the lat & lang from the field "l" and compare it with the current location lat & lang of the driver.
If the list's lat & lang is within 5 KMs , it will return the key , that is the parent name of the child "l".
Now I am currently in India. I use Lockito App to fix a Mock GPS location in India. After that I create a Ride within 5 KMs radius.
Now , the ride successfully listed in the drivers list. No Problem.
My Issue
But When I fix a mock GPS location in USA , this flow not working. I fixed the driver location on Brookyln Childeren's Museum and also created a ride on the same museum. But GeoQuery not calling onKeyEntered . It simply exists after calling GeoQueryReady function .
I check my current Lat & Lang from the app and it returns as 40.67434763453142,-73.94402358680964. I checked this against the lat&lang on field "l" .(refer above JSON). Both pointing to same location.
But GeoQuery failed to retrieve it
My Question
Why this issue happening ?. If I use India's locations. Geoquery works perfectly .
But why it failing in USA.
You may tell ,"There will be an issue in lockito". But Lockito returning correct location lat lang . I checked it in Logcat. And , if there is a issue in Lockitto then How it working in India.
Here My GeoQoery Code :
try {
showDialog();
double nearByDistanceRadius = 5.00d;
com.firebase.geofire.GeoFire geoFire = new com.firebase.geofire.GeoFire(FirebaseDatabase.getInstance().getReference().child(childtype));
final com.firebase.geofire.GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(latitude, longitude), nearByDistanceRadius);
geoQueryEventListener=new GeoQueryEventListener() {
#Override
public void onKeyEntered(final String key, GeoLocation location) {
Toast.makeText (getApplicationContext(), "The key is = ",Toast.LENGTH_LONG).show();
}
#Override
public void onKeyExited(final String key) {
System.out.println("ON KEYEXITED"+key);
}
#Override
public void onKeyMoved(String key, GeoLocation location) {
System.out.println("ON KEYMOVED"+key);
}
#Override
public void onGeoQueryReady() {
System.out.println("ON KEYQUERYREADY");
#Override
public void onGeoQueryError(DatabaseError error) {
System.out.println("ON KEYERROR"+error);
}
};
geoQuery.addGeoQueryEventListener(geoQueryEventListener);
} catch (Exception e) {
e.printStackTrace();
}
Above , except onGeoQueryReady , no method called if I use USA. But it's
working in India. Can anyone tell me why that is the case?
This maybe long gone n dusted but perhaps can help others.
Since u mention its mock GPS coordinates. and u probably manually entered them.
GeoFire uses a special g: "code" (10 digit code) which is area wise / city wise
if you use the same g code in the same city / country (India in ur case) it will work fine !
However,if you use the same code in another country , geoQuery will fail. To make it work. u need to add one line in your code
geoFire.SetLocation() and someone in USA needs to run that code once only. geoFire will save a special key in g: "" Location. After you have the key you can remove that line from your app.
You can use that key generated by geoFire to manually enter as many locations as you want in USA and geoQuery will always work on that !!! Make sure you use the same pattern as geoFire does.
g:
l:
0:
1:
It took a while but I figured. It's that buggy geofire dependency. Downgrade from 3.0.0 to 2.3.1 solved it.
I have a list of addresses and I want to mark this places all in google maps(nearly 1k), getting maybe its cordinates to save them.
Actually I am not programming an app, but I know both lenguages (Android and PHP) and I think it is possible to do this with both. Any idea ? Thank in advance!
Have you tried the google maps api?
https://developers.google.com/maps/documentation/geocoding/intro
https://developers.google.com/maps/documentation/geocoding/intro#ReverseGeocoding
This is what you are looking for I think.
EDIT: to mark stars on map
// In the following example, markers appear when the user clicks on the map.
// Each marker is labeled with a single alphabetical character.
var labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var labelIndex = 0;
function initialize() {
var bangalore = { lat: 12.97, lng: 77.59 };
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 12,
center: bangalore
});
// This event listener calls addMarker() when the map is clicked.
google.maps.event.addListener(map, 'click', function(event) {
addMarker(event.latLng, map);
});
// Add a marker at the center of the map.
addMarker(bangalore, map);
}
// Adds a marker to the map.
function addMarker(location, map) {
// Add the marker at the clicked location, and add the next-available label
// from the array of alphabetical characters.
var marker = new google.maps.Marker({
position: location,
label: labels[labelIndex++ % labels.length],
map: map
});
}
Good day!
I just want to ask if an InfoBubble can have a listener. I tried to insert the text of the InfoBubble in a div and insert an onclick command there, but nothing happens.
Thanks in advance. By the way I am currently developing an android application, and using webview to display the map.
ok, jetpro,
here's what I do in a similar scenario using javascript:
/* json data object requires:
data.Id,
data.Lat,
data.Lng,
data.Name,
data.Address,
data.Date
*/
function DisplayMapEvent(data, targetDiv) {
var latlng, options, map, contentString, infowindow, marker;
// setup our variables
latlng = new window.google.maps.LatLng(data.Lat, data.Lng);
options =
{
zoom: 15,
center: latlng,
mapTypeId: window.google.maps.MapTypeId.ROADMAP
};
// create the map inside our map <div> and apply the options
map = new window.google.maps.Map(document.getElementById(targetDiv), options);
map.setOptions(options);
// info to display in the infowindow popup
contentString =
'<div>' +
'<b style="color: #DC6852;">' + data.Name + '</b><br />' +
'Where: ' + data.Address + '<br />' +
'When: ' + data.Date + '<br />' +
'</div>';
// info/marker and popup objects setup
infowindow = new window.google.maps.InfoWindow({
content: contentString
});
marker = new window.google.maps.Marker({
position: latlng,
map: map
});
// add the usual suspect event handlers
window.google.maps.event.trigger(map, 'resize');
// to allow us to repoen a map marker infowindow if closed
window.google.maps.event.addListener(marker, 'click', function () {
infowindow.open(map, marker);
});
// open infowindow on map once rendered
infowindow.open(map, marker);
// sit back and wonder at the glory of google maps mow :-)
}
In my case, I'm passing a json object into the DisplayMapEvent function, but you could rejig this to your own requirement. This function is called anytime I wish to add a new marker onto my map, so you may be able to lift this wholesale and refactor as required.
Hope this helps.
Add listener like this
google.maps.event.addDomListener(infoBubble2.bubble_, 'click', function(){..});
Have fun!