Adding markers from url with Picasso - android

I'm adding markers to my map from a url using the Picasso library
As a marker isn't an ImageView I tried to use a Target instead
for(int x =0; x < mapIcon_url.length; x++){
Picasso.with(getActivity()).load(mapIcon_url[x]).resize(marker_size, marker_size+15).into(new Target() {
#Override
public void onSuccess(Bitmap b) {
bitmapMarker = BitmapDescriptorFactory.fromBitmap(b);
//create marker option
if(b != null)
markerOptions = new MarkerOptions().position(marker_position).icon(bitmapMarker));
else
markerOptions = new MarkerOptions().position(marker_position).icon(BitmapDescriptorFactory.fromResource(R.drawable.placeholder_pin)).snippet(String.valueOf(x));
marker = map.addMarker(markerOptions);
}
#Override
public void onError() {
//create marker option
markerOptions = new MarkerOptions().position(marker_position).icon(BitmapDescriptorFactory.fromResource(R.drawable.placeholder_pin)).snippet(String.valueOf(x));
marker = map.addMarker(markerOptions);
}
});
}
I'm doing this in a loop to add about 20 markers but I find that on first run of the code only 5 or 7 markers are added so I've switched to using the lib and an AsyncTask like this.
for(int x =0; x < mapIcon_url.length; x++){
new AddMarker().execute(mapIcon_url[x]);
}
public class AddMarker extends AsyncTask<String, Integer, BitmapDescriptor> {
BitmapDescriptor bitmapMarker1;
VenueDetails myVenue;
#Override
protected BitmapDescriptor doInBackground(String... url) {
myUrl = url[0];
try {
bitmapMarker1 = BitmapDescriptorFactory.fromBitmap(Picasso.with(getActivity()).load(myUrl).resize(marker_size, marker_size+15).get());
} catch (IOException e) {
e.printStackTrace();
}
return bitmapMarker1;
}
protected void onPostExecute(BitmapDescriptor icon) {
try {
map.addMarker(new MarkerOptions().position(marker_position).icon(icon)));
} catch (Exception e) {
e.printStackTrace();
}
}
}
However I'm a bit worried this method could give me some issues when I have alot of markers say about 100. My question would be is this the best way to do this and if not what other options can I try.

You have to keep a reference for each Target, otherwise the system
automatically releases them when the garbage collector is invoked.
So, the better solution is add each Target to a HashSet and then in
onBitmapLoaded() and onBitmapFailed methods from Target, remove the
Target itself from the set.
Thank you for the suggestion, now my code work perfectly. Below the pieces of code that implement your suggestion.
[...]//Global var
private Set<PoiTarget> poiTargets = new HashSet<PoiTarget>();
[...]
private void somewhere(){
PoiTarget pt;
for(Item item: data) {
m = map.addMarker(new MarkerOptions()
.position(new LatLng(item.latitude, item.longitude))
.title(item.title));
pt = new PoiTarget(m);
poiTargets.add(pt);
Picasso.with(context)
.load(mapImageURLString)
.into(pt);
}
}
[...]
//--------------------------------------------------------
// Inner class
//--------------------------------------------------------
class PoiTarget implements Target{
private Marker m;
public PoiTarget(Marker m) { this.m = m; }
#Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
m.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
poiTargets.remove(this);
Tools.msg(" #+ Set bitmap for "+m.getTitle()+" PT size: #"+poiTargets.size());
}
#Override public void onBitmapFailed(Drawable errorDrawable) {
Tools.msg(" #+ [ERROR] Don't set bitmap for "+m.getTitle());
poiTargets.remove(this);
}
#Override public void onPrepareLoad(Drawable placeHolderDrawable) {
}
}

You have to keep a reference for each Target, otherwise the system automatically releases them when the garbage collector is invoked.
So, the better solution is add each Target to a HashSet and then in onBitmapLoaded() and onBitmapFailed methods from Target, remove the Target itself from the set.

Related

Load image with Glide to Google Maps Marker Inside Async Task

I have been having a hard time trying to load url from Google Map Api into Google Maps Marker with Async Task. How do I correctly get this image loading library to load icons from google map api into the markers being shown in my custom map.
What is the best way to achieve this?
public class GetNearbyPlaces extends AsyncTask<Object, String,String> {
#Override
protected String doInBackground(Object... objects) {
mMap = (GoogleMap) objects[0];
String url = (String) objects[1];
DownloadUrl downloadUrl = new DownloadUrl();
try {
googlePlaceData = downloadUrl.ReadTheURL(url);
} catch (IOException e) {
e.printStackTrace();
}
return googlePlaceData;
}
private void DisplayNearbyPlaces(List<HashMap<String, String>> nearbyPlacesList){
for (int i = 0; i<nearbyPlacesList.size(); i++){
MarkerOptions markerOptions = new MarkerOptions();
//load image icon from url
String url = googleNearbyPlace.get("icon");
loadMarkerIcon(url);
}
}
private void loadMarkerIcon(final Marker marker, final String url) {
Glide.with(context).load(url)
.asBitmap().fitCenter().into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(#NonNull Bitmap bitmap, #Nullable Transition<? super Bitmap> transition) {
BitmapDescriptor icon = BitmapDescriptorFactory.fromBitmap(bitmap);
marker.setIcon(icon);
}
});
}
}

First GoogleMaps marker doesn't show, but the second one does

The marker has an icon property and it contains a bitmap of the user's Facebook profile picture.
I'm not really sure why the first marker doesn't load. But if you post a second, third or 4th (etc...) it works completely fine!
I don't have any other markers being added in my code other than the block of code below.
EDIT: This is important. The 'bitmap' value seems to be null on the first try. So it seems the if statement with the condition 'bitmap!=null' is catching it and preventing it from posting the first marker...
static Bitmap bitmap = null;
private Target loadtarget;
globalMap.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() {
#Override
public void onMapLongClick(LatLng point) {
vibe.vibrate(100);
AccessToken accessToken = AccessToken.getCurrentAccessToken();
MarkerOptions marker = new MarkerOptions().position(
new LatLng(point.latitude, point.longitude))
.title("New Marker");
Log.e("TAG", accessToken.getUserId());
String imageURL = new String("https://graph.facebook.com/" + accessToken.getUserId() + "/picture?type=small");
loadBitmap(imageURL);
if(bitmap!=null) {
globalMap.addMarker(marker
.position(point)
.icon(BitmapDescriptorFactory.fromBitmap(bitmap))
.anchor(0.5f, 1));
}
}
});
}
public void loadBitmap(String url) {
if (loadtarget == null) loadtarget = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
// do something with the Bitmap
handleLoadedBitmap(bitmap);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
Picasso.with(getActivity()).load(url).into(loadtarget);
}
public void handleLoadedBitmap(Bitmap b) {
bitmap = b;
}
You are right, bitmap will be null on the first long click because there's no time to load bitmap. You are calling load() and immediately after that trying to show bitmap.
You can load bitmap not in the long click listener but before, may be in the same place of code where you're setting that listener.
String imageURL = new String("https://graph.facebook.com/" + accessToken.getUserId() + "/picture?type=small");
loadBitmap(imageURL);
globalMap.setOnMapLongClickListener(
// ...
// the same code without imageURL initialization and bitmap loading
);
Follow the Documentation
static Bitmap bitmap = null;
private Target loadtarget;
globalMap.setOnMapLoadedCallback(new OnMapLoadedCallback() {
#Override
public void onMapLoaded() {
globalMap.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() {
#Override
public void onMapLongClick(LatLng point) {
vibe.vibrate(100);
AccessToken accessToken = AccessToken.getCurrentAccessToken();
MarkerOptions marker = new MarkerOptions().position(
new LatLng(point.latitude, point.longitude))
.title("New Marker");
Log.e("TAG", accessToken.getUserId());
String imageURL = new String("https://graph.facebook.com/" + accessToken.getUserId() + "/picture?type=small");
loadBitmap(imageURL);
if(bitmap!=null) {
globalMap.addMarker(marker
.position(point)
.icon(BitmapDescriptorFactory.fromBitmap(bitmap))
.anchor(0.5f, 1));
}
}
});
}
});
public void loadBitmap(String url) {
if (loadtarget == null) loadtarget = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
// do something with the Bitmap
handleLoadedBitmap(bitmap);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
Picasso.with(getActivity()).load(url).into(loadtarget);
}
public void handleLoadedBitmap(Bitmap b) {
bitmap = b;
}
I found out why it wasn't posting on the first try. It's because I was calling my method loadBitmap(..) too late, I felt like the process of it retrieving the image and loading it into the target value was too long for the clickListener which instantly places the marker. I just assumed that so I called it earlier, in my method onMapReady(), and that did the job.

Unable to add parse image to custom Google Map info window

I am trying to add an image I have in parse to a google map info window, I can add the image from resource but I am unable to load my existing parse image to the image view inside my infowindow. I have loaded the image from parse in other areas of my app, but it seems that the info window is out of scope. Is there a way to load this parse image inside my infowindow? I do not want to use a resource file because the images are different in parse. The 2 sections of my code are below:
class NearbyEventTask extends AsyncTask<String, Void, ArrayList<Item>>
{
Random r;
Context context;
public NearbyEventTask(Context context){
r = new Random();
this.context = context;
}
public LatLng getRandomLocation(Location center, double radius) {
// Convert radius from meters to degrees
double radiusInDegrees = radius / 111000;
double u = r.nextDouble();
double v = r.nextDouble();
double w = radiusInDegrees * Math.sqrt(u);
double t = 2 * Math.PI * v;
double lat = w * Math.cos(t);
double lon = w * Math.sin(t);
double new_lat = lat / Math.cos(center.getLongitude());
return new LatLng(new_lat + center.getLatitude(), lon + center.getLongitude());
}
#Override
protected ArrayList<Item> doInBackground(String... params) {
ArrayList<Item> list = new ArrayList<Item>();
ParseQuery<ParseObject> query = ParseQuery.getQuery("Places");
if(searchType!=null && searchType.length()>0) {
ArrayList<String> types = new ArrayList<String>();
for(String type: searchType.split("\\|")) types.add(type);
query.whereContainedIn("category", types);
}
if(lastKnownLocation!=null) {
query.whereNear("location", new ParseGeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude()));
}
try {
List<ParseObject> objects = query.find();
for(ParseObject obj : objects){
ParseGeoPoint point = obj.getParseGeoPoint("location");
Item item = new Item(obj.getString("name"), obj.getString("category"), obj.getString("description"), point.getLatitude(), point.getLongitude());
item.vicinity = obj.getString("description") + " | "+obj.getDate("event_date");
list.add(item);
if(obj.getParseFile("icon")!=null) {
item.setIcon(obj.getParseFile("icon").getUrl());
item.downloadIcon(context);
}
}
} catch (ParseException e) {
}
return list;
}
#Override
protected void onPostExecute(final ArrayList<Item> arrayList) {
if(isCancelled()) return;
if(googleMap!=null) {
googleMap.clear();
mMarker2Item.clear();
LatLngBounds.Builder boundBuilder = new LatLngBounds.Builder();
for (Item item : arrayList) {
MarkerOptions opts = new MarkerOptions()
.position(item.location())
.title(item.name);
if(item.iconBitmap!=null){
opts = opts.icon(BitmapDescriptorFactory.fromBitmap(item.iconBitmap));
}
Marker newMarker = googleMap.addMarker(opts);
newMarker.setSnippet(item.vicinity);
mMarker2Item.put(newMarker, item);
boundBuilder.include(item.location());
}
try {
if (firstTime) {
firstTime = false;
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(boundBuilder.build(), 200);
googleMap.moveCamera(cameraUpdate);
googleMap.animateCamera(cameraUpdate, 1000, null);
}
} catch (Exception ex) {
}
} else mHandler.postDelayed(new Runnable() {
#Override
public void run() {
onPostExecute(arrayList);
}
}, 500);
}
}
#Override
public void onMapReady(final GoogleMap googleMap) {
googleMap.setMyLocationEnabled(true);
this.googleMap = googleMap;
googleMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
// Use default InfoWindow frame
#Override
public View getInfoWindow(Marker arg0) {
return null;
}
// Defines the contents of the InfoWindow
#Override
public View getInfoContents(Marker marker) {
View v = getActivity().getLayoutInflater().inflate(R.layout.maps_infowindow, null);
v.setLayoutParams(new LinearLayout.LayoutParams((int) (mapFragment.getView().getMeasuredWidth() * .9), LinearLayout.LayoutParams.WRAP_CONTENT));
((TextView) v.findViewById(R.id.title)).setText(marker.getTitle());
((TextView) v.findViewById(R.id.desc)).setText(marker.getSnippet());
ImageView icon = (ImageView) v.findViewById(R.id.imageView5);
icon.getLayoutParams().height = 800; // OR
icon.getLayoutParams().width = 800;
ArrayList<Item> list = new ArrayList<Item>();
ParseQuery<ParseObject> query = ParseQuery.getQuery("Places");
if(searchType!=null && searchType.length()>0) {
ArrayList<String> types = new ArrayList<String>();
for(String type: searchType.split("\\|")) types.add(type);
query.whereContainedIn("icon", types);
}
try {
List<ParseObject> objects = query.find();
for(ParseObject obj : objects){
if(obj.getParseFile("icon")!=null) {
Picasso.with(getActivity()).load(obj.getParseFile("icon").getUrl()).into(icon, new MarkerCallback(marker));
}
}
} catch (ParseException e) {
}
return v;
}
}
);
CODE UPDATED: If you look at my NearbyEventTast class at the top, you can see how the code was implemented to get the data back from parse. I am trying to do the same thing by creating a new NearbyEventTast but I fail to load the correct image. It always shows the same image (I think its the first one in parse) and displays it for all objects instead of the corresponden image. Any idiea whats going on? Thank you!
As I am not much aware of Parse. But I have also face this issue to show a image from URL into info window.
First of all, the reason infowindow is not showing the downloaded image because MapFragment renders the view into a Canvas and then draws that. What you're seeing in the info window aren't the views you created, but a "picture" or "screenshot" of them. You basically need to call showInfoWindow() again on the Marker object, and that will re-render the Canvas and your image will now be visible.
Or you can use the Picasso Library to load the image. I am using the Picasso callback option in my app.
First you need to create a Class that will implements a Picasso Callback Interface and in the Constructor recieve a marker to call a show info window when image loads.
public static class MarkerCallback implements Callback {
private Marker marker;
public MarkerCallback(Marker marker) {
this.marker = marker;
}
#Override
public void onSuccess() {
if (marker != null && marker.isInfoWindowShown()) {
marker.hideInfoWindow();
marker.showInfoWindow();
}
}
#Override
public void onError() {
}
}
How to use it.
public View getInfoContents(Marker marker) {
View v = getActivity().getLayoutInflater().inflate(R.layout.maps_infowindow, null);
v.setLayoutParams(new LinearLayout.LayoutParams((int) (mapFragment.getView().getMeasuredWidth() * .9), LinearLayout.LayoutParams.WRAP_CONTENT));
((TextView) v.findViewById(R.id.title)).setText(marker.getTitle());
((TextView) v.findViewById(R.id.desc)).setText(marker.getSnippet());
ImageView markerIcon = (ImageView) v.findViewById(R.id.imageView5);
Picasso.with(MainActivity.this).load(imgUrl).into(markerIcon, new MarkerCallback(marker));
}
Hope this will help you.

Android - Released unknown imageData reference

I know exactly why I'm getting this error but I can't figure out how to fix it. So What I'm doing it drawing a bunch of markers using Picasso where I implement a Target and load the Marker Icon once the image is done download. However, the user is able to click a tab which would clear the map view. So in an effort to try and fix it I don't use getMap.clear() but rather track the markers in a hashmap and when the map should clear I loop through the hashmap and remove the marker from the map and clear the hashmap, however I'm still getting this issue.
Here is the function to remove the markers:
private void clearCars() throws IllegalArgumentException {
Iterator it = vehicles.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
Car aCar = (Car) pair.getValue();
if (aCar.getCurrentMarker() != null) {
if (aCar.getCurrentMarker().getmMarker() != null) {
aCar.getCurrentMarker().getmMarker().remove();
}
}
}
vehicles.clear();
}
This method draws the cars:
private void validateMap(ArrayList<Object> data) {
if (Theme.isCarTheme()) {
for (Object anObject : data) {
Car currentCar = (Car) anObject;
String uniqueIndex = currentCar.getCarID() + "-" + currentCar.getVehicleNumber();
CustomMarker aMarker = null;
if (vehicles.containsKey(uniqueIndex)) {
aMarker = ((Car) vehicles.get(uniqueIndex)).getCurrentMarker();
aMarker.getmMarker().setPosition(currentCar.getCarLocation());
} else {
MarkerOptions options = new MarkerOptions();
options.icon(BitmapDescriptorFactory.fromResource(R.drawable.blank_image));
options.position(currentCar.getCarLocation());
currentCar.setCurrentMarker(new CustomMarker(mapViewLayout.getMapView().getMap().addMarker(options)));
String strUrl = Constants.Car_IMAGE_URL + currentCar.getCarID() + assetType + ".png";
Picasso.with(getActivity()).load(strUrl).into(currentCar.getCurrentMarker());
vehicles.put(uniqueIndex, currentCar);
}
vehicles.get(uniqueIndex).setHasUpdated(true);
}
Iterator it2 = vehicles.entrySet().iterator();
while (it2.hasNext()) {
Map.Entry pair = (Map.Entry) it2.next();
Car aCar = (Car) pair.getValue();
if (aCar.isHasUpdated() == false) {
it2.remove();
aCar.getCurrentMarker().getmMarker().remove();
}
}
}
}
And this is the marker class:
public class CustomMarker implements Target {
Marker mMarker;
public CustomMarker(Marker marker) {
mMarker = marker;
}
#Override
public int hashCode() {
return mMarker.hashCode();
}
#Override
public boolean equals(Object o) {
if(o instanceof CustomMarker) {
Marker marker = ((CustomMarker) o).mMarker;
return mMarker.equals(marker);
} else {
return false;
}
}
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) throws IllegalArgumentException {
mMarker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
public Marker getmMarker() {
return mMarker;
}
}

Round icon to the marker in Google map v2 in android

I have implemented google maps in my android app. I have two questions related to markers.
Question 1: How to get a round profile image marker? i.e I would like to get roud image icon rather than square image icon in the mapmarker?
I am getting the list of lat, long, and profiepic link from a particular URL and loading them using Async. On Post execute I have
implemented a for loop
protected void onPostExecute(String file_url)
{
for (int i = 0; i < nameList.size(); i++)
{
userName = nameList.get(i).getName();
lati = nameList.get(i).getLati();
longi = nameList.get(i).getLongi();
photoUrl = nameList.get(i).getImage();
gotoLocation(Double.parseDouble(lati), Double.parseDouble(longi), userName, photoUrl, ZOOMVALUE);
}
}
I am loading the image using picasso library to the marker:
private void gotoLocation(double lat, double lng, String name, String url, float zoom)
{
final String uname = name;
curlat = lat;
curlong = lng;
Picasso.with(getActivity()).load(url).resize(100, 100).into(new Target()
{
#Override
public void onBitmapFailed(Drawable arg0)
{
markerOptions = new MarkerOptions().position(new LatLng(curlat, curlong)).icon(BitmapDescriptorFactory.fromResource(R.drawable.profilepic)).title(uname);
markerforfriends = googleMap.addMarker(markerOptions);
}
#Override
public void onBitmapLoaded(Bitmap b, LoadedFrom arg1)
{
bitmapMarker = BitmapDescriptorFactory.fromBitmap(b);
if(b != null)
{
markerOptions = new MarkerOptions().position(new LatLng(curlat, curlong)).icon(bitmapMarker).title(uname);
}
else
{
markerOptions = new MarkerOptions().position(new LatLng(curlat, curlong)).icon(BitmapDescriptorFactory.fromResource(R.drawable.profilepic)).snippet(uname);
}
markerforfriends = googleMap.addMarker(markerOptions);
}
#Override
public void onPrepareLoad(Drawable arg0)
{
}
});
}
I tried the following:
CircleOptions circle=new CircleOptions();
circle.center(ll).fillColor(Color.LTGRAY).radius(1);
googleMap.addCircle(circle);
This circles the current location but not the image?
Question 2: The above markers don't pop up always (not stable)? if I refersh the map it comes up properly? What could be wrong here? Is it because I am loading them using piccasso library? Is there different method to load the image from URL and them to marker?
This was an issue. See here: https://github.com/square/picasso/issues/308
According to Jake Wharton, don't declare Target as anonymous, as it gets garbage collected. Declare it as a private within your activity for example. See if that works.
On a separate file:
public class DummyTarget implements Target
{
public String uname;
//etc....
#Override
public void onBitmapLoaded(Bitmap bitmap, LoadedFrom from) {
// you codes....
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
// your codes....
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
}
Somewhere, declare it
private target = new DummyTarget();
And in your Picasso call(s):
private void gotoLocation(double lat, double lng, String name, String url, float zoom)
{
final String uname = name;
curlat = lat;
curlong = lng;
// set dummy target
target.uname = uname;
// etc...
Picasso.with(getActivity()).load(url).resize(100, 100).into(target);
You could also just load into "this"
Picasso.with(getActivity()).load(url).resize(100, 100).into(this);
and then have the current class implement Target
public class YourClass implements Target {
private ArrayList<BitMap> bitMaps;
private void gotoLocation(double lat, double lng, String name, String url, float zoom) {
final String uname = name;
curlat = lat;
curlong = lng;
Picasso.with(getActivity()).load(url).resize(100, 100).into(this);
}
#Override
public void onBitmapFailed(Drawable arg0) {
}
#Override
public void onBitmapLoaded(Bitmap bitmap, LoadedFrom loadedFrom) {
bitMaps.add(bitmap);
}
#Override
public void onPrepareLoad(Drawable arg0) {
}
}
Solved QUESTION 1 Part.. Since I am using picasso this became very useful for me to draw a circle icon.
Transformation transformation = new RoundedTransformationBuilder()
.borderColor(Color.WHITE)
.borderWidthDp(3)
.cornerRadiusDp(30)
.oval(false)
.build();
used RoundedTransformationBuilder Library!
Thanks to the creator of https://github.com/vinc3m1/RoundedImageView!
Thought this will be useful for others if they want to have something similar to MY QUESTION 1..

Categories

Resources