I was looking for a solution to my problem, i found some info here but i couldn't make it work with my code.
So, i have a google map with markers. The markers are created with JSON information. What i want is to attach a custom value to each marker, so each time a user touches the infowindow, a new activity will open with passed parameters attached to the specific marker.
Here is the JSON parsing and marker creation:
try{
JSONArray jArray = new JSONArray(result);
for(int i=0; i < jArray.length(); i++) {
JSONObject jObject = jArray.getJSONObject(i);
String image = jObject.getString("image");
String title = jObject.getString("title");
String snipet = jObject.getString("snipet");
double lat=jObject.getDouble("lat");
double lng=jObject.getDouble("lng");
addMarkers(lat, lng,image,title,snipet);
} // End Loop
} catch (JSONException e) {
Log.e("JSONException", "Error: " + e.toString());
} // catch (JSONException e)
private void addMarkers( double jLat, double jLng, final String image, final String jTitle,final String jDescription)
{
LatLng jLocation = new LatLng(jLat, jLng);
alertMarkers = theMap.addMarker(new MarkerOptions()
.position(jLocation)
.title(jTitle)
.snippet(jDescription)
.icon(BitmapDescriptorFactory.fromResource(icon)));
theMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
#Override
public void onInfoWindowClick(Marker arg0) {
Toast.makeText(MainActivity.this, arg0.getId(), 1000).show();
}}
}
How can assign that extra parameter, i.e image,to the marker so when a user touches the marker, the corresponding image will be used for further processing.
Thanks.
EDIT:
Here what i added to the code above,but something is not right:
Declared at start
HashMap<String, HashMap> extraMarkerInfo = new HashMap<String, HashMap>();
then added a new HashMap:
*alertMarkers = theMap.addMarker(new MarkerOptions()
.position(jLocation)
.title(jTitle)
.snippet(jDescription)
.icon(BitmapDescriptorFactory.fromResource(icon)));*
HashMap<String, Integer> data = new HashMap<String, Integer>();
data.put("id",alertID);
extraMarkerInfo.put(alertMarkers.getId(),data);
public void onInfoWindowClick(Marker arg0) {
HashMap<String, String> marker_data = extraMarkerInfo.get(arg0.getId());
Log.i("test",marker_data.get("id"));
(…)
But i am getting an error :
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at line:
HashMap<String, String> marker_data = extraMarkerInfo.get(arg0.getId());
I am bit confused with HashMaps.
You can maintain your own HashMap<>, mapping the ID value of the Marker (generated by the system, immutable) to its corresponding extra data. Then, on a marker tap, you look up the extra data. You can see this in action in this sample project.
Or, you can shove the string representation of the data (JSON) into the snippet of the Marker, and retrieve it again later on as needed. This will be more overhead, I suspect.
Marker, however, is final and cannot be extended.
Related
I am working with osmdroid library and android http request. So what I am doing is populating a map with markers where the lat and long are obtained from a database on the server. The question id: How can I call to marker.setOnClickListener out of the method where I do the request.
Here's my code:
RequestQueue queue = Volley.newRequestQueue(getApplicationContext());
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONArray obj = new JSONArray(response);
for (int i = 0; i < obj.length(); i++) {
JSONObject primer = obj.getJSONObject(i);
double latitud = primer.getDouble(TAG_Latitud);
double longitude = primer.getDouble(TAG_Longitude);
marker = new Marker(map);
marker.setPosition(new GeoPoint(latitud, longitude));
marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
mHashMap.put(marker, i);
Terrenos.add(terrenos);
}
marker.setOnMarkerClickListener(new Marker.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker, MapView mapView) {
Intent intent = new Intent(Osmdroid.this, Activity2.class);
startActivity(intent);
return false;
}
});
map.getOverlays().add(marker);
} catch (JSONException e) {
Toast.makeText(Osmdroid.this,"Something Wrong", Toast.LENGTH_LONG).show();
Log.w("exception", e.toString());
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(Osmdroid.this,"Something Wrong",Toast.LENGTH_LONG).show();
Log.w("Error", "No hay respuesta en http");
}
});
queue.add(stringRequest);
}
So, in the code above I create the marker inside the 'for', and I am calling the onClick method when it finished. But when I do this not all the marker works when on click, only the last one created.
Does somebody knows what to do? Please, I really would appreciate your help.
I'll try to explain in details, what are you doing wrong, because it seems that you are missing some basics about OOP programing, variables etc.
What's wrong:
You create multiple objects of type Marker in your loop, but variable named marker stores a reference to only one object of type Marker. You are reusing this variable in you loop end during each iteration you assign another instance of class Marker (another object of type Marker, with its own personal chunk of memory).
Once the loop finishes, your variable still exists and points to one object - and it's the last one Marker instance created, because it was the last value assigned to the variable named marker. You are assigning OnMarkerClickListener to this one and only object.
How to fix it
You want to call method setOnMarkerClickListener (assign OnMarkerClickListener) on every instance of Marker you have created. The way to do it is the same as with the creation: use a loop. You have basically 2 options here:
1) Reuse loop which is already in code and assign listener there. It's the simplest solution, because you can just move the assignment a little bit up in your source code and it would work:
for (int i = 0; i < obj.length(); i++) {
JSONObject primer = obj.getJSONObject(i);
double latitud = primer.getDouble(TAG_Latitud);
double longitude = primer.getDouble(TAG_Longitude);
marker = new Marker(map);
marker.setPosition(new GeoPoint(latitud, longitude));
marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
mHashMap.put(marker, i);
Terrenos.add(terrenos);
marker.setOnMarkerClickListener(new Marker.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker, MapView mapView) {
Intent intent = new Intent(Osmdroid.this, Activity2.class);
startActivity(intent);
return false;
}
});
}
map.getOverlays().add(marker);
This is not the most optimal way how to do it though. There is new instance of listener created for each marker, so a little bit of memory is wasted. But you can create an instance of listener before loop, store it in variable and assign the same object to all markers. Try to figure it out as an improvement, once you get things working.
2) You can store markers somewhere and create another loop where you'll assign the listener(s). You can have an variable (or a field in your class) containing list of all markers
List<Marker> markers = new ArrayList<>();
In loop, once a marker is created you can add it to this list:
for (int i = 0; i < obj.length(); i++) {
...
marker = new Marker(map);
marker.setPosition(new GeoPoint(latitud, longitude));
marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
markers.add(marker); //addind to the listt
...
Than you can iterate over the list later and assign listener to all of markers. Try to figure the code yourself. There will be similar decision as in previous case: you can create new listener for each marker or reuse one for all of them.
And the last thing: you don't need to craete new variable with list as I mentioned above, because you already have field which contains all your markers. You are using them as keys in a hasmmap:
mHashMap.put(marker, i);
I don't know what is a purpose of this hashmap, but you can get access to all markers (exactly: to an instance of Set with all markers) via mHasnMap.keySet()
Only the last marker has onClick because you set it just to it.
in every looping, the marker object gets a new reference marker = new Marker(map);. Because of that at the last looping, the marker object contains the last references and the onClick method sets just to it.
To solve that you just need to set the onClickand the map.getOverlays().add(marker) inside the loop.
public void onResponse(String response) {
try {
JSONArray obj = new JSONArray(response);
for (int i = 0; i < obj.length(); i++) {
JSONObject primer = obj.getJSONObject(i);
double latitud = primer.getDouble(TAG_Latitud);
double longitude = primer.getDouble(TAG_Longitude);
marker = new Marker(map);
marker.setPosition(new GeoPoint(latitud, longitude));
marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
map.getOverlays().add(marker);
mHashMap.put(marker, i);
Terrenos.add(terrenos);
marker.setOnMarkerClickListener(new Marker.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker, MapView mapView) {
Intent intent = new Intent(Osmdroid.this, Activity2.class);
startActivity(intent);
return false;
}
});
}
I am using the following to show a corresponding marker image based on the returned business name using Google Places and Maps:
if (name.contains("walmart")) {
mIcon = R.drawable.ic_wm_poi;
} if (name.contains("speedco")) {
mIcon = R.drawable.ic_poi_speedco;
}
The logcat shows "Unkown icon: business name" which does correspond with one of my if statements but doesn't show the corresponding icon. I've tried using if/else and switch statements. I've also tried name.contains, name.equals and name.contentEquals. For example, if I search "Walmart", the response returns the business name "Walmart" as shown in the logcat, and the marker shows the name "Walmart" once clicked yet the corresponding icon doesn't show. I am setting the marker icon like so:
markerOptions.icon(BitmapDescriptorFactory.fromResource(markerIcon(placeName)));
EDIT
After implementing crickets' comment, the icons for the various locations still don't show. This is what I have:
public class MarkerIcons {
public static HashMap<String, Integer> poiIcons;
public static HashMap<String, Integer> getpoiIcons(){
poiIcons = new HashMap<>();
poiIcons.put("Walmart", R.drawable.ic_wm_poi);
poiIcons.put("Walmart Supercenter", R.drawable.ic_wm_poi);
poiIcons.put("Walmart Neighborhood Market", R.drawable.ic_wm_poi);
return poiIcons;
}
public static Integer markerIcon(String name){
String TAG_MI = "marker";
Log.d(TAG_MI, "Loc Name: " + name);
int mIcon = 0;
if(getpoiIcons().containsKey(name)){
mIcon = getpoiIcons().get(name).intValue();
return mIcon;
}else{
mIcon = R.drawable.default_marker;
return mIcon;
}
}
}
How I am calling these functions:
Get Place Class
public class GetNearbyBusinessData extends AsyncTask<Object, String, String> {
private String TAG = "getplace";
private String googlePlacesData;
private GoogleMap mMap;
private String url;
#Override
protected String doInBackground(Object... params) {
try {
Log.d(TAG, "doInBackground entered");
mMap = (GoogleMap) params[0];
url = (String) params[1];
DownloadUrl downloadUrl = new DownloadUrl();
googlePlacesData = downloadUrl.readUrl(url);
Log.d(TAG, "doInBackground Exit");
} catch (Exception e) {
Log.d(TAG, e.toString());
}
return googlePlacesData;
}
#Override
protected void onPostExecute(String result) {
Log.d(TAG, "onPostExecute Entered");
List<HashMap<String, String>> nearbyPlacesList;
DataParser dataParser = new DataParser();
nearbyPlacesList = dataParser.parse(result);
ShowNearbyBusiness(nearbyPlacesList);
Log.d(TAG, "onPostExecute Exit");
Log.d(TAG, result);
}
private void ShowNearbyBusiness(List<HashMap<String, String>> nearbyPlacesList) {
for (int i = 0; i < nearbyPlacesList.size(); i++) {
Log.d(TAG,"Entered into showing business");
MarkerOptions markerOptions = new MarkerOptions();
HashMap<String, String> googlePlace = nearbyPlacesList.get(i);
double lat = Double.parseDouble(googlePlace.get("lat"));
double lng = Double.parseDouble(googlePlace.get("lng"));
String placeName = googlePlace.get("place_name");
String vicinity = googlePlace.get("vicinity");
LatLng latLng = new LatLng(lat, lng);
markerOptions.position(latLng);
markerOptions.title(placeName + "-" + vicinity);
markerOptions.icon(BitmapDescriptorFactory.fromResource(markerIcon(placeName)));
mMap.addMarker(markerOptions);
//move map camera
mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
mMap.animateCamera(CameraUpdateFactory.zoomTo(11));
}
}
}
EDIT 2
After setting break points for the markerIcon method and the line that calls the markerIcon method, the debug log shows that the returned response string is in fact "Walmart". Which is matching the key value in the HashMap. The returned icon for the marker is being returned as "0" though. It isn't returning the "value" from the hash map. I am setting the marker icon using this line:
mIcon = getpoiIcons().get(name).intValue();
Given the options available, I assumed that getting the matching "key" (in this case the string "name") would return its int "value". Am I missing something?
Even though the logcat shows the returned business names of Walmart, Walmart Supercenter and Walmart Neighborhood Market, it still shows the default marker. I have no idea why it is not showing the correct marker icon. Also, if it helps, my marker icons are vector images.
I think every thing work fine. but the error is, so far i have seen your code is,
markerOptions.icon(BitmapDescriptorFactory.fromResource(markerIcon(placeName)));
what you want to do is,
BitmapDescriptor icon = BitmapDescriptorFactory.fromResource(markerIcon(placeName)));
markerOptions.icon(icon);
Hope it helps.
U have function in separate class try this
MarkerIcons.markerIcon(placeName))
or update me if ur problem is cleared.
After trying the answers that were given and attempting to debug my code to find what was wrong, I found the error.
For anyone else that may have the same issue of the marker icons not showing, through trial and error I had found that you cannot use vector images that are in xml format as your markers. Once I switched them all over to png files, every icon showed without flaw.
Hope this answer helps someone else in the future. Thank you everyone for your contributions.
In an android application I want to check if a targetMarker is among the friendMarkers and if it is, then change that one friendMarker to the targetMarker, thus I don't have to remove the friendMarker and add the targetMarker. My question is how do I do this?
First, I need to convert the optionMarker array into a Marker array.
Second, I need to check the Id-s
Third, I need to transform the friendMarker into a targetMarker
MainActivity - I activate the method in the actionbar: (the storeMarker is not fully functioning, tries to store the optionMarker array in a Marker array - unsuccessfully.
if (id == R.id.get_target) {
// this is for getting the random target - and placing a marker on the Map
targetClass = randomTarget.getRandomTarget();
targetMarker = googleMap.addMarker(targetClass.marker);
// here I get the friendMarkers
MarkerOptions[] markers = randomTarget.getMarkers();
Marker [] array;
// the rest of the code is my try to convert the optionMarker array into Marker array
for (int j = 0; j < markers.length; j++){
// array[j] = storeMarkers().getId();
}
if (targetMarker.getId() == storeMarkers().getId()) {
}
}
And this is from the Target class - the optionMarker array
// puts the online player's location into an array of markeroptions
public MarkerOptions [] getMarkers() {
playerFunctions = new PlayerFunctions();
JSONArray array = playerFunctions.getAllOnline();
try {
MarkerOptions[] markers = new MarkerOptions[array.length()];
for(int i = 0; i < array.length(); i++) {
JSONObject element = array.getJSONObject(i);
markers[i] = new MarkerOptions()
.position(new LatLng(Double.parseDouble((String)element.get("lat")), Double.parseDouble((String)element.get("long"))))
.title((String)element.get("name"));
}
return markers;
} catch (JSONException e) {
e.printStackTrace();
return null;
}
}
Thanks for the replies.
Problem : My remote server returns 10 multiple value for a request.i parsed my response and using for loop i added markers(here i added info to title and snippet) on a map.(here i want to add extra data to marker so that i can access it in on info window click event)
in infoWindowClickListener i want to access that extra data.
How to add extra data to marker/ how to access ph data for a particular marker click(other wise i will get last value of ph in all markers).
i tried like this.
private class HttpGetTask extends AsyncTask<Void, Void, String>
{
//URL and http stuff
}
#Override
protected void onPostExecute(String result) {
try {
json = new JSONArray(result);
for (int i = 0; i < json.length(); i++) {
Log.v("Response", result);
final JSONObject e = json.getJSONObject(i);
String point = e.getString("point");
Log.v("POINT", point);//Checking points
String phone1 = e.getString("ph");
Log.v("PH", phone1);//Checking phone numbers
String[] point2 = point.split(",");//Splitting points
double lat1 = Double.parseDouble(point2[0]);
double lng1 = Double.parseDouble(point2[1]);
Log.v("LLDN", "" + lat1 + "&" + lng1);
//Adding multiple markers
//can i add extra information here along with title and snippet
gMap.addMarker(new MarkerOptions()
.title(e.getString("name"))
.snippet(
e.getString("LS")+""+e.getString("ph") )
.position(new LatLng(lng1, lat1))
.icon(BitmapDescriptorFactory
.fromResource(R.drawable.pmr)));
}
} catch (JSONException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
is it possible to attach extra value to a marker, along with title and snippet.?
or am i parsing in wrong way.?
While #worawee.s's will work perfectly fine, there is a more elegant solution that can allow you to add additional informations to a Marker object. So to solve this, you can use Marker's setTag (Object tag) method:
You can use this property to associate an arbitrary Object with this marker. For example, the Object can contain data about what the marker represents. This is easier than storing a separate Map. As another example, you can associate a String ID corresponding to the ID from a data set.
Not the best solution but this what I do in my application.
create markersMap as a private field in your activity/fragment.
private Map<Marker, ExtraDataObj> markersMap = new HashMap<Marker, ExtraDataObj>();
When you generate marker also put the marker and extra data in your markersMap
ExtraDataObj extraDataObj = new ExtraDataObj();
// extract and store all data you want in the extraDataObj
....
...
..
Marker marker = gMap.addMarker(new MarkerOptions()
.title(e.getString("name"))
.snippet(
e.getString("LS")+""+e.getString("ph") )
.position(new LatLng(lng1, lat1))
.icon(BitmapDescriptorFactory
.fromResource(R.drawable.pmr)));
markersMap.put(marker, extraDataObj);
in your onInfoWindowClick get extra data from the markersMap
ExtraDataObj extraDataObj = markersMap.get(arg0)
I have an iterates data from a JSONArray Object like this:
try{
for (int i = 0; i < arrResponse.length(); ++i){
JSONObject objResponse = arrResponse.getJSONObject(i);
Integer idShop = objResponse.getInt("ID");
String name = objResponse.getString("Name");
String address = objResponse.getString("Address");
Double slat = objResponse.getDouble("lat");
Double slong = objResponse.getDouble("long");
LatLng shopLocation = new LatLng(slat, slong);}}
I use the iterates data (idShop, name, address, etc) to mark the point of interest place on my map with this code below:
map.addMarker(new MarkerOptions()
.position(shopLocation)
.title(idShop+" "+name)
.snippet(address)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker_shop)));
Then i put a click listener into every object that created by marker with this code:
map.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {
#Override
public void onInfoWindowClick(Marker marker) {
// TODO Auto-generated method stub
Intent intent = new Intent(OrderActivity.this, Cart1Activity.class);
// intent.putExtra("IDShop", ""+idShop);
startActivity(intent);
}
});
How do I get the "idShop" data when I click on a marker, then passing it to intent? I use this code on setOnInfoWindowClickListener:
intent.putExtra("IDShop", ""+idShop);
But it doesn't get the real data.
With the code above you can simply get title from marker, split it on space character and take first element in the resulting array. This is your id.
int id = Integer.parseInt(marker.getTitle().split(" ")[0]);
Not very oriented on objects...