I have an app containing code to draw polylines between the source and destination points using the Google Maps Android SDK and the Polyline and PolylineOptions classes.
My question is that how can I add a small shape - for example, an arrow pointing from the source to the destination, drawn on the top of the polyline - so that its direction and angle change by zooming and rotating the map UI, much like the standard arrows in popular navigation apps?
Any help would be appreciated.
Anyway, you can create MapView- or MapFragment-based custom view and draw whatever you want (like in this answer). In that case you need to draw polyline on custom view canvas directly, not via using Google Maps Polyline.
If you want to draw single arrow at the middle of the path segment you need:
determine direction using, for example, computeHeading() method of SphericalUtil class of Maps SDK for Android Utility Library;
determine middle of path segment, for example, via interpolate() method of SphericalUtil class:
LatLng middle = interpolate(from, to, 0.5);
If you want to draw small arrows along all polyline you can customize line styles via can use setPathEffect() method of Paint class. For this You should create path for "arrow stamp" (in pixels), which will repeated every "advance" (in pixels too) - totally like in this example for "diamond":
mPathDiamondStamp = new Path();
mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2, 0);
mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2);
mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2, 0);
mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2);
mPathDiamondStamp.close();
mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2 + DIAMOND_BORDER_WIDTH, 0);
mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2 + DIAMOND_BORDER_WIDTH / 2);
mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2 - DIAMOND_BORDER_WIDTH, 0);
mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2 - DIAMOND_BORDER_WIDTH / 2);
mPathDiamondStamp.close();
mPathDiamondStamp.setFillType(Path.FillType.EVEN_ODD);
mDiamondPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mDiamondPaint.setColor(Color.BLUE);
mDiamondPaint.setStrokeWidth(2);
mDiamondPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mDiamondPaint.setStyle(Paint.Style.STROKE);
mDiamondPaint.setPathEffect(new PathDashPathEffect(mPathDiamondStamp, DIAMOND_ADVANCE, DIAMOND_PHASE, PathDashPathEffect.Style.ROTATE));
In that case all work for "direction and angle change by zooming and rotating the map UI" processing Paint class do for you "from the box" - you just need to set the "stamp" pattern and intervals between stamps.
For converting LatLng coordinates into screen coordinates you can use toScreenLocation() of Projection class. So, make custom view based on MapView or SupportMapFragment and draw whatever you want with Canvas and Paint.
Here and there you can also find good examples of setPathEffect() usage.
I'm using a MapView to display a Google Map and the trying to create a circle overlay. But for some reason the circle border is displayed but it is not filled with any color, here is what is generated using the android emulator:
Here is an idea of what I would like to achieve (i created it through the w3school online editor).
Here is the code:
LatLng center = new LatLng(-16.272425327210556,166.4380745618525);
double radius = 9944545.500957435;
CircleOptions co = new CircleOptions().center(center).clickable(false).radius((radius)).fillColor(Color.YELLOW).visible(false);
this.marker_twilight_civil = map.addCircle(co);
I tried with a different radius value and actually it works with a specific one, here i created 3 circles with different radius values, and olny one is filled, the other two are not filled.
Here is the code for the circle that is filled correctly
LatLng center = new LatLng(-16.272425356472187,166.43807456511806);
double radius = 8006044.77150472;
CircleOptions co = new CircleOptions().center(center).clickable(false).radius((radius)).fillColor(Color.GREEN).visible(true);
this.marker_night = map.addCircle(co);
Could it be a issue with the too large radius? But on the web version is working well.
It could be because the radius is too large. Google Maps uses a Mercator projection, which will skew circles if they are large enough. For technical reasons, if the shape isn't "closed" then the fill won't be applied. If the shape is too large, or too close to the highly-skewed top or bottom of the map, then the shape will break open and the fill will be ignored.
If it was truly important you could try forcing a custom Projection on Google Maps (which could avoid the skew and keep the shape closed), but this isn't a well-tread path and I can't provide a solution beyond the link:
https://developers.google.com/maps/documentation/javascript/examples/map-projection-simple
I would like to change the colors of the areas of Google Maps.
Here's an example
Is there a way I could do that?
Hi you can use Polygon for draw shape.
https://developers.google.com/android/reference/com/google/android/gms/maps/model/Polygon
Another possible way is GroundOverlay.
You can overlay an image for particular area over the map (under markers, polygons, polylines).
https://developers.google.com/maps/documentation/android-api/groundoverlay
update: How to create a ground overlay layer?
For example: you want to fill this building as green.
You need to get latitude and longitude pairs for two positions : southwest and northeast.
Then create an image with transparent (such as PNG format).
After that, you need to create a ground overlay in your code.
LatLngBounds bounds = new LatLngBounds(
new LatLng(....), // South west corner
new LatLng(....) // North east corner
);
GroundOverlayOptions myBuilding = new GroundOverlayOptions()
.image(BitmapDescriptorFactory.fromResource(R.drawable.my_building))
.positionFromBounds(bounds);
You should see the target building is painted as green.
I want to make maps with custom markers. In API v2 I can set icon, title, etc for markers. But I want to display title with marker at the first onset. Now title displays only when I taping the marker. In v1 was overlays, but in v2 I didn't found anything similar.
Edited:
Maybe I was not clear enough. Something like Marker.showInfoWindow() in API works only for one marker. I can't show info windows for all of my markers at the same time. Anyway I need to show titles for all of my markers, without waiting while user will tap on it.
I have also stumbled upon this problem. V2 API is a step forward, two steps back. Google, please add an overridable 'draw' method on the Marker or GoogleMap classes so we can customize the drawing ourselves.
A possible solution is to generate the bitmap on the fly and attach it to the marker. i.e. Create a canvas, insert the marker bitmap, draw the text next to the marker. This involves some painful calculations (the appropriate canvas size with the marker bitmap and the text next to each other). Unfortunately, there's no setIcon method in Marker, so every time the text changes, a new marker has to be created. It may be fine if you just have a marker on the map, but with dozens of markers, this may not be feasible. Also there may be memory issue on creating those bitmaps dynamically. A sample code (with just the text):
Bitmap.Config conf = Bitmap.Config.ARGB_8888;
Bitmap bmp = Bitmap.createBitmap(200, 50, conf);
Canvas canvas = new Canvas(bmp);
canvas.drawText("TEXT", 0, 50, paint); // paint defines the text color, stroke width, size
mMap.addMarker(new MarkerOptions()
.position(clickedPosition)
//.icon(BitmapDescriptorFactory.fromResource(R.drawable.marker2))
.icon(BitmapDescriptorFactory.fromBitmap(bmp))
.anchor(0.5f, 1)
);
Hopefully, Google will add the appropriate methods so we can do this easily. Damn, I really like the new Map rotate feature in V2 API.
Finally did it. So what you do is have a background image (in my case i just use a blue rectangle).
Create a marker like so:
Marker myLocMarker = map.addMarker(new MarkerOptions()
.position(myLocation)
.icon(BitmapDescriptorFactory.fromBitmap(writeTextOnDrawable(R.drawable.bluebox, "your text goes here"))));
Notice the writeTextOnDrawable() Method:
private Bitmap writeTextOnDrawable(int drawableId, String text) {
Bitmap bm = BitmapFactory.decodeResource(getResources(), drawableId)
.copy(Bitmap.Config.ARGB_8888, true);
Typeface tf = Typeface.create("Helvetica", Typeface.BOLD);
Paint paint = new Paint();
paint.setStyle(Style.FILL);
paint.setColor(Color.WHITE);
paint.setTypeface(tf);
paint.setTextAlign(Align.CENTER);
paint.setTextSize(convertToPixels(context, 11));
Rect textRect = new Rect();
paint.getTextBounds(text, 0, text.length(), textRect);
Canvas canvas = new Canvas(bm);
//If the text is bigger than the canvas , reduce the font size
if(textRect.width() >= (canvas.getWidth() - 4)) //the padding on either sides is considered as 4, so as to appropriately fit in the text
paint.setTextSize(convertToPixels(context, 7)); //Scaling needs to be used for different dpi's
//Calculate the positions
int xPos = (canvas.getWidth() / 2) - 2; //-2 is for regulating the x position offset
//"- ((paint.descent() + paint.ascent()) / 2)" is the distance from the baseline to the center.
int yPos = (int) ((canvas.getHeight() / 2) - ((paint.descent() + paint.ascent()) / 2)) ;
canvas.drawText(text, xPos, yPos, paint);
return bm;
}
public static int convertToPixels(Context context, int nDP)
{
final float conversionScale = context.getResources().getDisplayMetrics().density;
return (int) ((nDP * conversionScale) + 0.5f) ;
}
Thanks to Arun George:
Add text to image in android programmatically
Seems like your question was finally answered at a Google I/O session.
Have a look at http://googlemaps.github.io/android-maps-utils/
It has:
Marker clustering — handles the display of a large number of points
Heat maps — display a large number of points as a heat map
IconGenerator — display text on your Markers (see screenshot)
Very importantly it can be modified on any thread so handling many markers is a breeze
I am not sure which you are trying to achieve: having the info window show up without the user have to tap on the marker, or using a completely different view for the info window (or perhaps both).
To show the info window without requiring a user tap:
I haven't tested this myself, but I'm guessing Marker.showInfoWindow() would do the trick (assuming the Marker's visibility is already true.
To provide a custom view for the InfoWindow
There are two options here and you should refer to the documentation on GoogleMap.InfoWindowAdapter:
public static interface GoogleMap.InfoWindowAdapter
Provides views for
customized rendering of info-windows.
Methods on this provider are called when it is time to show an info
window for a marker, regardless of the cause (either a user gesture or
a programmatic call to showInfoWindow(). Since there is only one info
window shown at any one time, this provider may choose to reuse views,
or it may choose to create new views on each method invocation.
When constructing an info-window, methods in this class are called in
a defined order. To replace the default info-window, override
getInfoWindow(Marker) with your custom rendering. To replace just the
info-window contents, inside the default info-window frame (the
callout bubble), leave the default implementation of
getInfoWindow(Marker) in place and override getInfoContents(Marker)
instead.
Basically, whether you override getInfoWindow() or getInfoContents() will depend on whether or not you just wish to customize what you see inside the callout bubble, or whether you wish to customize the entire info window view, including an alternative to the callout bubble.
One caveat: I believe when you override these methods, it performs a simple rendering of what the view looks like at the time getInfoWindow() or getInfoContents() is called. I myself am interested in trying to replicate the look of the native Google Maps Android app which has a little "directions" icon next to the name of the place. One of the problems I believe (see here: https://stackoverflow.com/a/13713536/129475) is that if you have something like a button in your view, it may not behave like a button because of the static rendering.
Customize the marker image
You can replace the default marker image with a custom marker image, often called an icon. Custom icons are always set as a BitmapDescriptor, and defined using one of four methods in the BitmapDescriptorFactory class.
fromAsset(String assetName)
Creates a custom marker using an image in the assets directory.
fromBitmap (Bitmap image)
Creates a custom marker from a Bitmap image.
fromFile (String path)
Creates a custom icon from a file at the specified path.
fromResource (int resourceId)
Creates a custom marker using an existing resource.
The below snippet creates a marker with a custom icon.
private static final LatLng MELBOURNE = new LatLng(-37.813, 144.962);
private Marker melbourne = mMap.addMarker(new MarkerOptions()
.position(MELBOURNE)
.title("Melbourne")
.snippet("Population: 4,137,400")
.icon(BitmapDescriptorFactory.fromResource(R.drawable.arrow)));
This simple code works to display the title without requiring a click event:
googleMap.addMarker(new MarkerOptions()
.position(latLng)
.title(String title))
.showInfoWindow();
Google has come out with the IconGenerator class which makes adding custom icon easy.
IconGenerator iconGenerator = new IconGenerator(mContext);
Bitmap bitmap = iconGenerator.makeIcon("Text Of Icon");
mMap.addMarker(new MarkerOptions()
.position(latlng)
.title("Location")
.icon(BitmapDescriptorFactory.fromBitmap(bitmap)));
Why don't you keep an array of the markers then when it comes to laoding the markers, iterate through them calling showInfoWindow(). May not be the most elegant solution but it does what you are saying I think.
I solved this problem by making my own markers with a picture editor which has the details below. It took some time to make, but it works.
I used Photoshop and 53x110px markers.
I'm currently programming very simple game for Android (API level 7) to discover and learn the android SDK. This game involve drawing shape on the screen that will change colour when touched.
Some shapes may embed one or several holes. My issue is : if I touch the shape, the whole thing's colour change, even the holes'. Here is the pseudo code I use, shape is the polygon I want to draw, boundary it's outer boundary, holes an array of its holes. Hole and boundary hold an array of their points.
Path MyPath = Path();
Path.moveTo(boundary.points[0].x, boundary.point[0].x);
for (point in boundary) {
MyPath.lineTo(point.x, point.y);
}
Path.close();
for (hole in shape.holes) {
MyPath.moveTo(hole.points[0].x,hole.points[0].y);
for (point in hole) {
MyPath.lineTo(point.x, point.y);
}
MyPath.close();
}
// setting Paint here...
canvas.drawPath(MyPath, MyPaint);
Is their something I'm missing regarding Path in Android or do you have some alternative way to do it?
Are you sure you are using the correct path filling rule? If you are using for example WINDING as filling rule the holes must be in the opposite directions in respect to the outer boundary (e.g. border counter-clockwise and holes clockwise)