Drawing transparent on a MapView - android

I am currently drawing lines on a MapView based on different GeoPoints to indicate sectors. With the following code (this is within an overlay):
#Override
public void draw(Canvas canvas, MapView mapView, boolean shadow)
{
for(Polygon polygonTemp : polygonList)
{
Path p = new Path();
Projection projection = mapView.getProjection();
boolean firstTime = true;
for(GeoPoint geoPoint : polygonTemp.getGeoPointList())
{
Point drawPoint = new Point();
projection.toPixels(geoPoint, drawPoint);
if(firstTime)
{
p.moveTo(drawPoint.x, drawPoint.y);
firstTime = false;
}
else
{
p.lineTo(drawPoint.x, drawPoint.y);
}
}
p.setFillType(Path.FillType.EVEN_ODD);
Paint polyPaint = new Paint();
polyPaint.setStrokeWidth(1);
polyPaint.setStyle(Paint.Style.FILL_AND_STROKE);
polyPaint.setAntiAlias(true);
polyPaint.setColor(Color.parseColor(polygonTemp.getColor()));
canvas.drawPath(p, polyPaint);
firstTime = true;
}
super.draw(canvas, mapView, shadow);
}
The problem is, I want them to be filled with some degree of transparency, so I can still see the map under the filled sectors. I tried to set polyPaint.setAlpha(), even to 255 (which should be completely transparent) and it doesn't do anything, it's completely opague.
Anyone knows what I'm doing wrong?

I don't see where you are setting the alpha. Regardless 255 is not transparent, it is opaque.
FYI, I am doing identical things (drawing paths on map overlays) and this works fine for drawing a 50% opaque, red line:
mPaint.setColor(Color.parseColor ("#88ff0000"));

Related

How to cache android.graphics.path or Bitmap when using Overlays?

I'm using an Overlay to mark areas on Google Maps by drawing a shape of ten thousands of GeoPoints I get from any source. This works and looks like this:
#Override
public void draw(android.graphics.Canvas canvas, MapView mapView, boolean shadow) {
super.draw(canvas, mapView, false);
Projection projection = mapView.getProjection();
List<Zone> zones = ApplicationContext.getZones();
path.rewind();
for (Zone zone : zones) {
paint.setDither(true);
paint.setStyle(Style.FILL);
paint.setAlpha(40);
MultiPolygon multiPolygon = zone.getMultiPolygon();
List<Polygon> polygons = multiPolygon.getPolygons();
for (Polygon polygon : polygons) {
for (List<Coordinate> coordinates : polygon.getCoordinates()) {
for (int i = 0; i < coordinates.size(); i++) {
Point p = new Point();
projection.toPixels(new GeoPoint((int)(coordinates.get(i).getLatitude() * 1E6), (int)(coordinates.get(i).getLongitude() * 1E6)), p);
if (i == 0) {
path.moveTo(p.x, p.y);
}
else {
path.lineTo(p.x, p.y);
}
}
}
}
}
canvas.drawPath(path, paint);
}
The problem is that this is very resource consuming. Every time one scrolls or moves the map on MapView, the path has to be calculated over and over again, because the pixel coordinates have been changed. The drawn area could become so big that the scrolling on the MapView is so slow that it is functional unusable.
My ideas are
to somehow cache the "shape" the path generates and just redraw it
when the zoom level changes on the MapView.
to somehow draw the painting on an "on the fly"-Bitmap to use it as Overlay (maybe as ItemizedOverlay), listen for MapView scrolling and move the bitmap by the scrolled distance.
I'm not sure if there are better methods.
Any ideas how I could solve this problem?
(I'm using Google Maps API 1 and can't change).
Before resorting to trying to figure out how to match the map's movement, there are some optimizations to your current code that will probably yield significant savings. In particular, these two lines inside your inner loop is executed the most times, but fairly expensive to execute (two memory allocations, floating point multiplies, and four method calls).
Point p = new Point();
projection.toPixels(new GeoPoint((int)(coordinates.get(i).getLatitude() * 1E6), (int)(coordinates.get(i).getLongitude() * 1E6)), p);
First, you only ever need one Point object, so avoid allocating it in your loop. Move it to just below your path.rewind();
Second, if you pre-computed your coordinates as GeoPoints instead of computing them each time, you would save a lot of processing in your draw routine. You can also get rid of that if statement with a little work. Assuming you preconvert your list of coordinate to a list of GeoPoint, and make it available through polygon.getGeoCoordinates(), you could end up with your inner loops looking like -
for (List<GeoPoint> geoCoordinates : polygon.getGeoCoordinates()) {
projection.toPixels(geoCoordinates.get(0),p);
path.moveTo(p.x, p.y); // move to first spot
final List<GeoPoint> lineToList = geoCoordinates.sublist(1,geoCoordinates.size()); // A list of all the other points
for(GeoPoint gp : lineToList) {
projection.toPixels(gp, p);
path.lineTo(p.x, p.y);
}
}
And that will run a lot faster than what you were doing before.
After tinkering around in the last days I found a possible solution (and I don't think there is a better one) to not draw the path over and over again but move it to the current position.
The difficult part was to figure out how to cache the drawn shape to not calculate it over and over again. This can be done by using a Matrix. With this Matrix (I imagine this as some kind of "template") you can manipulate the points coordinates inside the path. The first time (when someone starts moving the Map) I draw the area as usual. When it tries to calculate it the second time or more, I don't redraw the shape but I manipulate the path by calculating the "delta" from the current point to the last point. I know what the current point is, because I always map the original GeoPoint (which always stays the same) to the point which results from the current projection. The "delta" needs to be set as Matrix. After that I transform the path by using this new Matrix. The result is really very fast. The scrolling of the Map is as fast as without using an Overlay.
This looks like this (this is no production code, and it cannot deal with zooming yet, but it shows the principle I use as basis for my optimizations):
public class DistrictOverlay extends Overlay {
// private final static String TAG = DistrictOverlay.class.getSimpleName();
private Paint paint = new Paint();
private Path path = new Path();
private boolean alreadyDrawn = false;
private GeoPoint origGeoPoint;
Point p = new Point();
Point lastPoint = new Point();
#Override
public void draw(android.graphics.Canvas canvas, MapView mapView, boolean shadow) {
super.draw(canvas, mapView, false);
Projection projection = mapView.getProjection();
List<Zone> zones = ApplicationContext.getZones();
if (!alreadyDrawn) {
path.rewind();
for (Zone zone : zones) {
if (!zone.getZoneId().equals(MenuContext.getChosenZoneId())) {
continue;
}
String dateString = zone.getEffectiveFrom().trim().replace("CEST", "").replace("GMT", "").replace("CET", "").replace("MESZ", "");
if (DateUtil.isBeforeCurrentDate(dateString)) {
paint.setColor(Color.RED);
} else {
paint.setColor(Color.GREEN);
}
paint.setDither(true);
paint.setStyle(Style.FILL);
paint.setAlpha(40);
MultiPolygon multiPolygon = zone.getMultiPolygon();
List<Polygon> polygons = multiPolygon.getPolygons();
for (Polygon polygon : polygons) {
for (List<GeoPoint> geoPoints : polygon.getGeoPoints()) {
projection.toPixels(geoPoints.get(0), p);
path.moveTo(p.x, p.y);
origGeoPoint = new GeoPoint(geoPoints.get(0).getLatitudeE6(), geoPoints.get(0).getLongitudeE6());
lastPoint = new Point(p.x, p.y);
final List<GeoPoint> pathAsList = geoPoints.subList(1, geoPoints.size());
for (GeoPoint geoPoint : pathAsList) {
projection.toPixels(geoPoint, p);
path.lineTo(p.x, p.y);
}
}
}
}
}
else {
projection.toPixels(origGeoPoint, p);
Matrix translateMatrix = new Matrix();
translateMatrix.setTranslate(p.x - lastPoint.x, p.y - lastPoint.y);
path.transform(translateMatrix);
lastPoint = new Point(p.x, p.y);
}
canvas.drawPath(path, paint);
if (!path.isEmpty()) {
alreadyDrawn = true;
}
}
#Override
public boolean onTap(GeoPoint p, MapView mapView) {
return true;
}
}

Update Location Marker and draw path on Google Maps when walking/driving in Android

I want to create a functionality like Google Maps. I want to update my location marker whenever a person start walking/driving. I am thinking of using onLocationChange method of LocationListner but I am not sure. I also like to draw the path with updating location on the Google Maps.
All suggestions are most welcome.
ItemizedOverlay:-
public class LocationOverlay extends ItemizedOverlay<OverlayItem>{
private List<OverlayItem> myOverlays;
Path path;
static int cnt = -1;
public LocationOverlay(Drawable defaultMarker) {
super(boundCenterBottom(defaultMarker));
System.out.println("Normal...");
myOverlays = new ArrayList<OverlayItem>();
}
public void addOverlay(OverlayItem overlay) {
System.out.println("Add Overlay called");
myOverlays.add(overlay);
populate();
cnt++;
}
#Override
protected OverlayItem createItem(int i) {
return myOverlays.get(i);
}
public void removeItem(int i) {
myOverlays.remove(i);
populate();
}
#Override
public int size() {
return myOverlays.size();
}
#Override
public void draw(Canvas canvas, final MapView mapView, boolean shadow) {
if (shadow) {
paint.setDither(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(4);
path = new Path();
Point point1 = new Point();
Projection projection = mapView.getProjection();
if (myOverlays.size() > 1) {
System.out.println(">>>>>>>>>>>");
for (int i = 0, j=i; i < myOverlays.size(); i++) {
Point point1 = new Point();
Point point2 = new Point();
projection.toPixels(myOverlays.get(i).getPoint(), point1);
System.out.println(">>>>>>>>>>>");
projection.toPixels(myOverlays.get(j++).getPoint(), point2);
System.out.println("Point == " + j);
path.moveTo(point2.x, point2.y);
path.lineTo(point1.x, point1.y);
canvas.drawPath(path, paint);
super.draw(canvas, mapView, shadow);
}
}
}
canvas.drawPath(path, paint);
super.draw(canvas, mapView, shadow);
}
}
NOTE - draw method gets called multiple time for single locationChanged.
EDIT :-
I am able to draw the route whenever there is any location changed event fire but I am facing a small problem there. Its also drawing path from root every time. I want that from last place it should start drawing the path (i.e connecting from last to next location). I am attaching one image for better understanding.
From the image you can see that the starting point is also connected to last point. I don't want this. Any idea on this.
Current location can be easily drawn with MyLocationOverlay. What you need to do - is to add this overlay to your MapView. Something like:
mapView.addOverlay(new MyLocationOverlay(context, mapView));
The default location dot will be drawn automatically and will be updated as soon as you move
With route it is a bit trickier, but still there is no rocket science in there. What you need to do - is to extend Overlay class and implement draw() logic. You need to keep track of GeoPoints you travelled and project them on a map (in your overlay's draw() you can ask your mapView for a Projection instance - getProjection(). You can use this projection to map GeoPoints to your map coordinates).
As soon as you have list of GeoPoints - you can create a Path and draw this Path on a Canvas
The simplified example how to draw path on a map (there are some custom classes I use in my project, but you should get an idea):
Path path;
#Override
public void draw(Canvas canvas, final MapView mapView, boolean shadow)
{
Projection proj = mapView.getProjection();
updatePath(mapView,proj);
canvas.drawPath(path, mPaint);
}
protected void updatePath(final MapView mapView, Projection proj)
{
path = new Path();
proj.toPixels(mPoints.get(0).point, p);
path.moveTo(p.x, p.y);
for(RouteEntity.RoutePoint rp : mPoints)
{
proj.toPixels(rp.point, mPoint);
path.lineTo(mPoint.x, mPoint.y);
}
}

why show only gridline in google map?

I am developing Google map in my application but its not showing map only showing grid line ,Please anyone help me what is problem is that?
And I am drawing text over bubbles,when more than 2 bubble lie same places,in that case text overlapping to each other.
After changing API key Map will be showing but my second problem still remaining.I have to draw bubbles on Map and text (size of property on that bubble) suppose there are size of list 4 then 4 should be draw on bubble.I have been implemented these but there is no desire output is showing.
When more than 2 property on same position and each have more than 2 list size then text on bubble overlapp to eachother sorry for my explanation I am using this code -
drawable = this.getResources().getDrawable(R.drawable.bluedot);
drawable1 = this.getResources().getDrawable(R.drawable.bluedot_large);
overlay = new GoogleMapViewOverlay(drawable,mapView,activity);
//GoogleMapViewOverlay method is as:-
public GoogleMapViewOverlay(Drawable drawable, MapView mapView,
Activity activity2)
{
//super(drawable);
super(boundCenterBottom(drawable));
setLastFocusedIndex(-1);
populate();
arrayList1 = new ArrayList<Applicationdataset>() ;
items = new ArrayList<OverlayItem>();
marker = drawable;
mContext = mapView.getContext();
mc = mapView.getController();
this.activity = activity2;
DB = new DatabaseHelper(activity2);
}
//add item as
if(point != null)
{
if(arrayList.get(i).getUnitstotal().equalsIgnoreCase("1"))
{
oi = overlay.createItem(i);
System.out.println("listing when 1 value is"+arrayList.get(i).getUnitstotal());
overlay.addItem(oi);
}
else if(!arrayList.get(i).getUnitstotal().equalsIgnoreCase("1"))
{
oi = overlay.createItem(i);
System.out.println("listing when more value is "+ arrayList.get(i).getUnitstotal());
if(overlay!=null)
{
overlay.addItem(oi);
//overlay.draw(canvas, mapView, shadow);
}
}
}
//draw method for text draw over bubbles
#Override
public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when )
{
super.draw(canvas, mapView, false);
arrayList1 = arrayList;
for (int index = 0; index < items.size(); index++)
{
OverlayItem item = items.get(index);
GeoPoint point = item.getPoint();
Point ptScreenCoord = new Point();
mapView.getProjection().toPixels(point, ptScreenCoord);
//Paint
Paint paint = new Paint();
paint.setStyle(Style.STROKE);
paint.setTextAlign(Paint.Align.CENTER);
paint.setTypeface((Typeface.defaultFromStyle(Typeface.BOLD)));
paint.setTextSize(20);
paint.setColor(Color.WHITE);
paint.setFilterBitmap(false);//true
if(arrayList.get(index).getUnitstotal().equalsIgnoreCase("1"))
canvas.drawText("", ptScreenCoord.x, ptScreenCoord.y-20,paint);
else
{
canvas.drawText(arrayList.get(index).getUnitstotal().toString(), ptScreenCoord.x, ptScreenCoord.y-40, paint);
}
}
return true;
}
Most Probably you havent fetched google map api key, please fetch it according to the instructions on following link, and then try:
https://developers.google.com/maps/documentation/android/mapkey

Strange behaviour with Osmdroid overlay at high zoom levels

I'm using the osmdroid 3.0.5 jar in my app to display a map view. I'm superimposing an overlay, consisting of horizontal and vertical lines. I've noticed that in certain locations only, at high zoom levels some of the lines disappear, then reappear as the map is dragged.
I've produced a minimal sample app which demonstrates the problem which exhibits itself both on a real device and an emulator (Gingerbread 2.3.3).
The complete code is shown below - (the 'transform' methods are necessary in the real app although they don't appear to be in this minimal sample) :
public class DemoMap extends Activity implements MapViewConstants {
private MapView mapView;
private MapController mapController;
private MapOverlay mmapOverlay = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.copymain);
mapView = (MapView) findViewById(R.id.mapview);
mapView.setTileSource(TileSourceFactory.MAPNIK);
mapView.setBuiltInZoomControls(true);
mapView.setMultiTouchControls(true);
mapController = mapView.getController();
mapController.setZoom(17);
// Only shows the bug at ceratin lat/lon positions, this is one
GeoPoint point2 = new GeoPoint(39191699, -120102561);
mapController.setCenter(point2);
mmapOverlay = new MapOverlay(this);
List<Overlay> listOfOverlays = mapView.getOverlays();
listOfOverlays.add(mmapOverlay);
mapView.invalidate();
}
public class MapOverlay extends org.osmdroid.views.overlay.Overlay {
public MapOverlay(Context ctx) {super(ctx); }
private int mVpl;// viewport left, top, right, bottom
private int mVpt;
private int mVpr;
private int mVpb;
private MapView mMv = null;
// Two routines to transform and scale between viewport and mapview
private float transformX(float in, MapView mv) {
float out;
out = ((mVpr - mVpl) * in) / (mv.getRight() - mv.getLeft())
+ mVpl;
return out;
}
private float transformY(float in, MapView mv) {
float out;
out = ((mVpb - mVpt) * in) / (mv.getBottom() - mv.getTop())
+ mVpt;
return out;
}
#Override
protected void draw(Canvas pC, MapView mapV, boolean shadow) {
if (shadow)
return;
Paint paint;
paint = new Paint();
paint.setColor(Color.RED);
paint.setAntiAlias(true);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(1);
paint.setTextAlign(Paint.Align.LEFT);
paint.setTextSize(12);
final Rect viewportRect = new Rect();
final Projection projection = mapV.getProjection();
viewportRect.set(projection.getScreenRect());
mVpl = viewportRect.left;
mVpt = viewportRect.top;
mVpr = viewportRect.right;
mVpb = viewportRect.bottom;
// draw two lines to split screen into 2x2 quarters
// drag the map left and right and the vertical line disappears,
// then reappears! It's OK at one less zoom level
pC.drawLine(transformX(mapV.getWidth()/2, mapV), transformY(0, mapV),
transformX(mapV.getWidth()/2, mapV),
transformY(mapV.getHeight(), mapV), paint);
pC.drawLine(transformX(0, mapV), transformY(mapV.getHeight()/2, mapV),
transformX(mapV.getWidth(), mapV),
transformY(mapV.getHeight()/2, mapV), paint);
}
}
}
Interestingly if, in my real app, if I do the drawing to an offscreen bitmap, then draw it all in one go at the end of the Overlay's draw, it's OK the lines don't disappear.
Any help will be much appreciated.
I know this is an old question, but I believe what you're experiencing is Issue 221. This has been fixed in version 3.0.9.
Also see: OSMDroid PathOverlay drawing is corrupted at high zoom levels
Sounds like it may be another manifestation of this bug:
Issue 207
Do you see the same behaviour with earlier API versions?

How to make route drawing more efficient?

This is the code I'm using to draw route. When i have 1000 of points, route severely slows ui. Maybe someone could provide a code snippet or a link which explains how to do route drawing more efficiently? I know that one way to solve this is caching path to bitmap, but have no idea how to do it.
public class PathOverlay extends Overlay{
private GeoPoint startPoint;
private GeoPoint finishPoint;
private ArrayList<GeoPoint> pathPoints;
private Paint paint;
private Path path;
private Point pathStartPoint;
private Point pathEndPoint;
private float dx;
private float dy;
public PathOverlay(GeoPoint startPoint, GeoPoint finishPoint, ArrayList<GeoPoint> pathPoints, int color){
this.startPoint = startPoint;
this.finishPoint = finishPoint;
this.pathPoints = pathPoints;
this.paint = new Paint();
this.paint.setAntiAlias(true);
this.paint.setDither(true);
this.paint.setColor(color);
this.paint.setAlpha(150);
this.paint.setStrokeWidth(4);
this.paint.setStyle(Paint.Style.STROKE);
}
#Override
public void draw(Canvas overlayCanvas, MapView mapView, boolean shadow) {
if(path == null) {
path = getPath(mapView);
} else {
path = transformPath(mapView);
}
overlayCanvas.drawPath(path, paint);
super.draw(overlayCanvas, mapView, shadow);
}
private Path getPath(MapView mapView) {
Projection projection = mapView.getProjection();
if(path == null) {
path = new Path();
path.setFillType(FillType.WINDING);
} else {
path.rewind();
}
Point point = new Point();
pathStartPoint = new Point();
pathEndPoint = new Point();
projection.toPixels(startPoint, point);
projection.toPixels(startPoint, pathStartPoint);
path.moveTo(point.x, point.y);
path.addCircle(point.x, point.y, (float) 2.0, Direction.CCW);
if (pathPoints != null) {
for(int i=0;i<pathPoints.size();i++) {
projection.toPixels(pathPoints.get(i), point);
path.lineTo(point.x, point.y);
}
}
projection.toPixels(finishPoint, point);
projection.toPixels(finishPoint, pathEndPoint);
path.lineTo(point.x-5, point.y);
path.addCircle(point.x-5, point.y, (float) 2.0, Direction.CCW);
return path;
}
private Path transformPath(MapView mapView) {
Projection projection = mapView.getProjection();
Point sPoint = new Point();
Point ePoint = new Point();
projection.toPixels(startPoint, sPoint);
projection.toPixels(finishPoint, ePoint);
float sx = ((float)ePoint.x - (float)sPoint.x)/((float)pathEndPoint.x - (float)pathStartPoint.x);
float sy = ((float)ePoint.y - (float)sPoint.y)/((float)pathEndPoint.y - (float)pathStartPoint.y);
if(sx != 1.0 && sy != 1.0) {
Log.i("PathOverlay", "resized");
return getPath(mapView);
} else {
Log.i("PathOverlay", "moved");
Matrix matrix = new Matrix();
dx = (float)sPoint.x - (float)pathStartPoint.x;
dy = (float)sPoint.y - (float)pathStartPoint.y;
matrix.postTranslate(dx, dy);
pathStartPoint = sPoint;
pathEndPoint = ePoint;
path.transform(matrix);
return path;
}
}
}
You can draw the path to a transparent Bitmap object (whatever size you see fitting - the bigger it is, the better the detail of the path yet higher memory consumption).
Make sure you create it with Bitmap.config.ARGB_8888 for transparency.
Once you've done this, you'll be using two rectangles to display the path on the Overlay:
A source rectangle to determine which part of the path is visible
A destination rectangle to determine where you want to display this piece of the path on the Overlay's canvas.
You'll be using Canvas.drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
Shouldn't be too difficult, you've done most of the important calculations in your transformPath method.
Added:
You can actually do a combination of both holding a path drawn to a Bitmap and redrawing the actual path points. Use the technique described above for when the user moves around the map or zooms in/out then redraw the path when the user lets go of the screen.
Best way to increase speed of drawing is reducing number of point the path includes. Probably they are not necessary - lot of them just lays between previous and next, so you can filter them by:
minimal distance from previous point (easy way)
minimal bearing change (a bit harder, although Location class and it's method bearingTo() should help.
You should know that draw() method draws one draw cycle about 50 times. I do not know why, but you can test it. This slows down the performance. And if you have to draw 1000 object and draw() draws them 30-50 each...it is getting very very clumsy..
To avoid this, you should create a cache overlay.This cache will draw all objects only once and will reject the other draws.
To speed up the process, draw in background thread with low priority.
The fastest way to draw a line between multiple points is to use the method Canvas.drawLines(float[] pts, int offset, int count, Paint paint).
I have tested all methods and this is the fastest method that android offers.

Categories

Resources