106

How can we achieve a map marker icon with vector asset file, the way google shows it like this, programatically:

map

Update:

map.addMarker(new MarkerOptions()
    .position(latLng)
    .icon(BitmapDescriptorFactory.fromResource(R.drawable.your_vector_asset))
    .title(title);

this doesnot work when dealing with vector assets. The main reason to ask the question. The error with the above code:

java.lang.IllegalArgumentException: Failed to decode image. The provided image must be a Bitmap.

Shuddh
  • 1,920
  • 2
  • 19
  • 30
  • @RishabhMahatha I can easily do it with png, but the problem is png is a heavy file. either I have to store it in my apk or have to call it from server. But I specifically need it from vector asset file. – Shuddh Feb 21 '17 at 11:17
  • @LucaNicoletti I have to achieve this using vector asset(or SVG) file. and I am not getting any lib or method to do it. – Shuddh Feb 21 '17 at 11:19
  • 1
    It can also be found in this answer: https://stackoverflow.com/questions/33696488/getting-bitmap-from-vector-drawable – Erfan Aug 08 '20 at 03:01
  • there is an extension method Drawable.toBitmap() you can just it with BitmapDescriptorFactory.fromBitmap() – gturedi Oct 06 '21 at 14:17

9 Answers9

167

You can use this method:

private BitmapDescriptor bitmapDescriptorFromVector(Context context, int vectorResId) {
        Drawable vectorDrawable = ContextCompat.getDrawable(context, vectorResId);
        vectorDrawable.setBounds(0, 0, vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight());
        Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        vectorDrawable.draw(canvas);
        return BitmapDescriptorFactory.fromBitmap(bitmap);
}

So your code will look like:

map.addMarker(new MarkerOptions()
                .position(latLng)
                .icon(bitmapDescriptorFromVector(getActivity(), R.drawable.your_vector_asset))
                .title(title);

Edit:
In Kotlin it may look like:

private fun bitmapDescriptorFromVector(context: Context, vectorResId: Int): BitmapDescriptor? {
        return ContextCompat.getDrawable(context, vectorResId)?.run {
            setBounds(0, 0, intrinsicWidth, intrinsicHeight)
            val bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)
            draw(Canvas(bitmap))
            BitmapDescriptorFactory.fromBitmap(bitmap)
        }
    }
Leo DroidCoder
  • 14,527
  • 4
  • 62
  • 54
50

I was looking for exact same requirement, and seeing this question made me happy at first, but same as @Shuddh I wasn't happy with the given answers.

To make my story short, I am using following code for this requirement:

private BitmapDescriptor bitmapDescriptorFromVector(Context context, @DrawableRes  int vectorDrawableResourceId) {
    Drawable background = ContextCompat.getDrawable(context, R.drawable.ic_map_pin_filled_blue_48dp);
    background.setBounds(0, 0, background.getIntrinsicWidth(), background.getIntrinsicHeight());
    Drawable vectorDrawable = ContextCompat.getDrawable(context, vectorDrawableResourceId);
    vectorDrawable.setBounds(40, 20, vectorDrawable.getIntrinsicWidth() + 40, vectorDrawable.getIntrinsicHeight() + 20);
    Bitmap bitmap = Bitmap.createBitmap(background.getIntrinsicWidth(), background.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    background.draw(canvas);
    vectorDrawable.draw(canvas);
    return BitmapDescriptorFactory.fromBitmap(bitmap);
}

and a usage example:

.icon(bitmapDescriptorFromVector(this, R.drawable.ic_car_white_24dp));

Note: you may wish to use different bounding for your vectors, my vectors were 24dp in size and I've used a 48dp png image(blue-part, which can be a vector too) as background.

UPDATE: Adding screenshot as it was requested.

Screenshot for end result

SidMorad
  • 954
  • 12
  • 19
  • I think this will do the trick. I will check this and Mark the answer. But if you have a screenshot. Please share. – Shuddh Jan 20 '18 at 17:42
  • I've add a screenshot, I hope that clarify what was the requirement originally! ;-) "I mean reuse vectors inside a Map pin" – SidMorad Jan 21 '18 at 07:02
  • 3
    I think that calculations for the inner image bounds should be different (kotlin implementaion below): `val left = (background.intrinsicWidth - vectorDrawable.intrinsicWidth) / 2` `val top = (background.intrinsicHeight - vectorDrawable.intrinsicHeight) / 3` `vectorDrawable.setBounds(left, top, left + vectorDrawable.intrinsicWidth, top + vectorDrawable.intrinsicHeight)` – gswierczynski Nov 15 '18 at 13:44
  • They look really low-resolution – xjcl Jul 02 '20 at 20:01
  • @xjcl I did guess someone will say that! :) and that's why I add this part to my answer: "I've used a 48dp png image(blue-part, ***which can be a vector too***) as background" – SidMorad Jul 04 '20 at 07:27
9

Here is the code for kotlin lovers ;)

private fun bitMapFromVector(vectorResID:Int):BitmapDescriptor {
    val vectorDrawable=ContextCompat.getDrawable(context!!,vectorResID)
    vectorDrawable!!.setBounds(0,0,vectorDrawable!!.intrinsicWidth,vectorDrawable.intrinsicHeight)
    val bitmap=Bitmap.createBitmap(vectorDrawable.intrinsicWidth,vectorDrawable.intrinsicHeight,Bitmap.Config.ARGB_8888)
    val canvas=Canvas(bitmap)
    vectorDrawable.draw(canvas)
    return BitmapDescriptorFactory.fromBitmap(bitmap)
}
Kasun Thilina
  • 1,523
  • 2
  • 14
  • 20
8

If someone who is looking for in kotlin here is the method for you :

  private fun  bitmapDescriptorFromVector(vectorResId:Int):BitmapDescriptor {
            var vectorDrawable = ContextCompat.getDrawable(requireContext(), vectorResId);
            vectorDrawable!!.setBounds(0, 0, vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight());
            var bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
            var canvas =  Canvas(bitmap);
            vectorDrawable.draw(canvas);
            return BitmapDescriptorFactory.fromBitmap(bitmap);
}

above method will convert you vector icon to bitmapdescritor

map.addMarker(new MarkerOptions()
                .position(latLng)
                .icon(bitmapDescriptorFromVector(getActivity(), R.drawable.your_vector_asset))
                .title(title)

and this one for setting marker for your map thanks for Leo Droidcoder from his answer only i have converted it to kotlin

canerkaseler
  • 6,204
  • 45
  • 38
M.Yogeshwaran
  • 1,419
  • 4
  • 22
  • 48
6

Might be a bit late to the game but this works great with Google Maps v2:

public static BitmapDescriptor getBitmapFromVector(@NonNull Context context,
                                                   @DrawableRes int vectorResourceId,
                                                   @ColorInt int tintColor) {

    Drawable vectorDrawable = ResourcesCompat.getDrawable(
            context.getResources(), vectorResourceId, null);
    if (vectorDrawable == null) {
        Log.e(TAG, "Requested vector resource was not found");
        return BitmapDescriptorFactory.defaultMarker();
    }
    Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
            vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    DrawableCompat.setTint(vectorDrawable, tintColor);
    vectorDrawable.draw(canvas);
    return BitmapDescriptorFactory.fromBitmap(bitmap);
}

Initialized as:

locationMarkerIcon = LayoutUtils.getBitmapFromVector(ctx, R.drawable.ic_location_marker,
                ContextCompat.getColor(ctx, R.color.marker_color));

Usage:

googleMap.addMarker(MarkerOptions().icon(getMarkerIcon()).position(latLng));

Note: getMarkerIcon() just returns the initialized non null locationMarkerIcon member variable.

Screenshot:

enter image description here

prerak
  • 399
  • 4
  • 8
  • 1
    It's not what the question asks for. If you can see the screenshot attached at correct answer. – Shuddh Apr 11 '18 at 17:22
  • 2
    @Shuddh basically my vector asset is a bit different in my screenshot (based on my project) but as far as I see, your original question asked for how to use a vector asset as a marker icon and that's what the static method does (along with also adding a color dynamically to it). – prerak Apr 12 '18 at 16:14
  • It's not adding colour dynamically. It's basically the change in the inner icon. If you can see the screenshot in selected answer – Shuddh Apr 12 '18 at 16:16
  • Supperb! In my case I got rid of the tint color and it works! Additionally, I'd suggest modifyng marker's anchor as follows (if your's it's a classic pin): `.anchor(0.5f, 1f);` – joninx Jun 25 '20 at 12:23
6

In Kotlin: I used the below code to show the SVG image on Marker. Here, I used no background color / SVG.

fun getBitmapDescriptorFromVector(context: Context, @DrawableRes vectorDrawableResourceId: Int): BitmapDescriptor? {

    val vectorDrawable = ContextCompat.getDrawable(context, vectorDrawableResourceId)
    val bitmap = Bitmap.createBitmap(vectorDrawable!!.intrinsicWidth, vectorDrawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bitmap)
    vectorDrawable.setBounds(0, 0, canvas.width, canvas.height)
    vectorDrawable.draw(canvas)

    return BitmapDescriptorFactory.fromBitmap(bitmap)
}

Use like this way:

googleMap?.addMarker(MarkerOptions().position(LatLng(it.latitude!!, it.longitude!!))
            .title(it.airLineDetails))?.setIcon(
            getBitmapDescriptorFromVector(requireContext(), R.drawable.ic_flight_blue))

Screen Shot:

Shihab Uddin
  • 6,699
  • 2
  • 59
  • 74
2

convert vector resource to bitmap object and use BitmapDescriptorFactory.fromBitmap(bitmap)

   Bitmap bitmap = getBitmapFromVectorDrawable(getContext(),R.drawable.ic_pin);
   BitmapDescriptor descriptor =BitmapDescriptorFactory.fromBitmap(bitmap);
   MarkerOptions markerOptions = new MarkerOptions();
   markerOptions.icon(descriptor);

Bitmap converter:

 public static Bitmap getBitmapFromVectorDrawable(Context context, int drawableId) {
        Drawable drawable =  AppCompatResources.getDrawable(context, drawableId)
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            drawable = (DrawableCompat.wrap(drawable)).mutate();
        }

        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }
Shahab Saalami
  • 862
  • 10
  • 18
-1

Try this

MarkerOptions op = new MarkerOptions();
op.position(src_latlng);
Marker origin_marker = googleMap.addMarker(op);

Bitmap bitmap = getBitmap(this,R.drawable.ic_map_marker);
origin_marker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));

getBitmap

public Bitmap getBitmap(Context context, int drawableId) {
   Drawable drawable = ContextCompat.getDrawable(context, drawableId);
   if (drawable instanceof BitmapDrawable) {
       return BitmapFactory.decodeResource(context.getResources(), drawableId);
   } else if (drawable instanceof VectorDrawable) {
       return getBitmap((VectorDrawable) drawable);
   } else {
       throw new IllegalArgumentException("unsupported drawable type");
   }
}

ic_map_marker.xml

<vector android:height="32dp" android:viewportHeight="512.0"
    android:viewportWidth="512.0" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="#f32f00" android:pathData="M288,284.8V480l-64,32V284.8c10.3,2.1 21,3.3 32,3.3S277.7,286.9 288,284.8zM384,128c0,70.7 -57.3,128 -128,128c-70.7,0 -128,-57.3 -128,-128S185.3,0 256,0C326.7,0 384,57.3 384,128zM256,64c0,-17.7 -14.3,-32 -32,-32s-32,14.3 -32,32s14.3,32 32,32S256,81.7 256,64z"/>
</vector>
Eswar
  • 172
  • 1
  • 9
  • Can you explain the vectorDrawable return statement. What is the getBitmap method you are using. – Shuddh Feb 21 '17 at 12:56
  • Plus I don't think this will also give me the marker I want. as the marker shown in the image is default marker containing a vector asset. but this code will generate icon as per the vector asset, but not with-in the marker(the blue part). – Shuddh Feb 21 '17 at 12:59
  • @Shuddh The icon that you are looking for would have to be custom. The vector asset should define the blue and the white car part. And then you will add that vector as icon. – Q2x13 Feb 22 '17 at 04:20
  • @PradumnKumarMahanta So I have to create another vector for background of icon. Right? But still how to set a vector as marker? – Shuddh Feb 22 '17 at 05:59
  • @Shuddh No, You can create a vector for the complete thing. Marker + Image. And Add that vector as marker with the method stated by, Eswar or as Hiristo Stoyanov and I suggested, both are working for me. – Q2x13 Feb 22 '17 at 06:09
  • I am getting the error as shown in the question. Plus I need different kind of images in that marker. – Shuddh Feb 22 '17 at 10:07
-1

For a Kotlin user.Please check below code.As I ddid in Fragment class.

class MapPinFragment : Fragment() {

    private lateinit var googleMap1: GoogleMap

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_map_pin, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        mapView.onCreate(savedInstanceState)
        mapView.onResume()

    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)


        mapView.getMapAsync { googleMap ->
            googleMap1 = googleMap as GoogleMap
            addCustomMarker()
        }

    }

    private fun addCustomMarker() {
        Log.d("addCustomMarker", "addCustomMarker()")
        if (googleMap1 == null) {
            return
        }
        // adding a marker on map with image from  drawable
        googleMap1.addMarker(
            MarkerOptions()
                .position(LatLng(23.0225 , 72.5714))
                .icon(BitmapDescriptorFactory.fromBitmap(getMarkerBitmapFromView()))
        )
    }

    override fun onDestroy() {
        super.onDestroy()
        if (mapView != null)
            mapView.onDestroy()
    }
    override fun onLowMemory() {
        super.onLowMemory()
        mapView.onLowMemory()
    }

    private fun getMarkerBitmapFromView(): Bitmap? {
        val customMarkerView: View? = layoutInflater.inflate(R.layout.view_custom_marker, null)
//        val markerImageView: ImageView =
//            customMarkerView.findViewById<View>(R.id.profile_image) as ImageView
        customMarkerView?.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED );
        customMarkerView?.layout(0, 0, customMarkerView.measuredWidth, customMarkerView.measuredHeight);
        customMarkerView?.buildDrawingCache();
        val returnedBitmap = Bitmap.createBitmap(
            customMarkerView!!.measuredWidth, customMarkerView.measuredHeight,
            Bitmap.Config.ARGB_8888
        )
        val canvas = Canvas(returnedBitmap)
        canvas.drawColor(Color.WHITE, PorterDuff.Mode.SRC_IN)
        val drawable = customMarkerView.background

        drawable?.draw(canvas);
        customMarkerView.draw(canvas);
        return returnedBitmap;

    }




}
Kush
  • 1,080
  • 11
  • 15