So I have an custom overlay item that I have written to fill in a transparent blue overlay based around an array of geo points
#Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
Projection projection = mapView.getProjection();
Paint fill = new Paint();
fill.setColor(Color.BLUE);
fill.setAlpha(50);
fill.setStyle(Paint.Style.FILL_AND_STROKE);
Path path = new Path();
Point firstPoint = new Point();
projection.toPixels(geoPoints.get(0), firstPoint);
path.moveTo(firstPoint.x, firstPoint.y);
for (int i = 1; i < geoPoints.size(); ++i) {
Point nextPoint = new Point();
projection.toPixels(geoPoints.get(i), nextPoint);
path.lineTo(nextPoint.x, nextPoint.y);
}
path.lineTo(firstPoint.x, firstPoint.y);
path.setLastPoint(firstPoint.x, firstPoint.y);
canvas.drawPath(path, fill);
super.draw(canvas, mapView, shadow);
}
What I need is a way to get the center point of this overlay so I can place a marker on it,
anyone have any ideas?
although i am not familiar with android framework, i assume you writing in java and using some kind of google maps api. But i do familiar with graphics and geo development. My suggestion to you firsst of all to check whether the standard api has some kind of
getBounds(path) that returns to you RectangularBounds object or similar. Then from rectangular bounds you can ask for bounds.getCenter() which returns the center of bounds as geo point or other metric. If you use pixels just convert the geopoint like you did...
If getBounds doesn't exists in api (what is hard to believe), just implement a simple interface , you can find a lot of examples on the net.
simple pseudo code for finding the bounds of a geo shape for geo points, if you need pixels use x,y respectively:
bounds = { topLeft: new GeoPoint(path[0]), bottomRight: new GeoPoint(path[0])};
for( point in path ){
bounds.topLeft.lat = max( bounds.topLeft.lat,point.lat );
bounds.topLeft.lng = min( bounds.topLeft.lng,point.lng );
bounds.bottomRight.lat = min( bounds.bottomRight.lat,point.lat );
bounds.bottomRight.lng = max( bounds.bottomRight.lng,point.lng );
}
bounds.getCenter(){
return new GeoPoint(rectangle center point);
// i am sure you will able to manage the code here )))
}
hope this will help
Related
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;
}
}
I'm working on a simple Android app for plotting routes on a map. All is going well, but I have an issue when zooming in on my Samsung Galaxy S2. It works fine on a Galaxy S3, so I'm wondering whether it's related to memory management on the lower specced device. It also works fine on the emulator.
Here is equivalent code located in the overlays onDraw method, just condensed for posting here:
Point current = new Point();
Path path = new Path();
Projection projection = mapView.getProjection();
Iterator<GeoPoint> iterator = pointList.iterator();
if (iterator.hasNext()) {
projection.toPixels(iterator.next(), current);
path.moveTo((float) current.x, (float) current.y);
} else return path;
while(iterator.hasNext()) {
projection.toPixels(iterator.next(), current);
path.lineTo((float) current.x, (float) current.y);
}
Paint roadPaint = new Paint();
roadPaint.setAntiAlias(true);
roadPaint.setStrokeWidth(8.0f);
roadPaint.setColor(Color.BLACK);
roadPaint.setStyle(Paint.Style.STROKE);
canvas.drawPath(path, roadPaint);
It's not too dissimilar to most of the sample code floating around for doing this. I'm just wondering if anyone can confirm my suspicions and advise if there is anything I can do in terms of configuration or tweaks that I can do to force drawing at all zoom levels?
Thanks in advance.
Cheers,
Nathan
The problem is that you are painting the overlay yourself for a very specific state of the mapview. You should use OverlayItem instead.
The OverlayItem is added to the MapView overlays collection, and the MapView handles all the re-drawing depending on it's own state ( zoom, location, etc )
#Override
public void draw( Canvas canvas, MapView mapView, boolean shadow )
{
super.draw( canvas, mapView, shadow );
int x1 = -1;
int y1 = -1;
int x2 = -1;
int y2 = -1;
Paint paint = new Paint();
paint.setStyle( Paint.Style.STROKE );
paint.setColor( GeoLocation.ROUTE_COLOR );
paint.setStrokeWidth( STROKE_WIDTH );
for ( int i = 0; i < mRouteGeoPoints.size(); i++ )
{
Point point = new Point();
mapView.getProjection().toPixels( geoPoints.get( i ), point );
x2 = point.x;
y2 = point.y;
if ( i > 0 )
{
canvas.drawLine( x1, y1, x2, y2, paint );
}
x1 = x2;
y1 = y2;
}
}
You said that code above was an equivalent (not the real code you are running) and that's clear because you are returning a Path object in a onDraw() which you couldn't.
The "compressed form" of code you show should work as well as using the drawLine(). So the problem should come from something else (may the original code).
Anyway, I'll give you a couple of hints:
When the top and bottom of object you are drawing to a canvas are both out of screen, the object is ignored and not drawn. Check if this is not whats happening with your path. See my answer in this post Android Map Overlay Disappears on Zoom
You don't need to rebuild the path object every time. You are probably already doing it, and that's why you made the short version above. See my answer in this post with some suggestions to improve path drawing: Overlay behavior when zooming
If for some reason you really want to use the slower approach of drawLine(), you can use the follwing to make the line look better:
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setColor(...);
paint.setAlpha(...);
paint.setStrokeWidth(...);
Finally, if the issue remains, update your question with more relevant code and let me know. Maybe I can help further.
Regards.
I have an application that gets the current position and the user selects a destination, these two starting locations, it queries Google:
https://developers.google.com/maps/documentation/directions/
And get an xml with the places where I "turn" to reach the destination
After I read the xml, and trace a path between first and second point of the xml, and so on
Before the change of API, the path was perfect in the streets, but now appears like this image:
The code I use to draw:
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
super.draw(canvas, mapView, shadow);
Projection proj = mapView.getProjection();
Point ponto1, ponto2;
Path caminho = new Path();
for (int i = 0; i < geoPoints.size() - 1; i++) {
ponto1 = proj.toPixels(geoPoints.get(i), null);
ponto2 = proj.toPixels(geoPoints.get(i + 1), null);
caminho.moveTo(ponto2.x, ponto2.y);
caminho.lineTo(ponto1.x,ponto1.y);
canvas.drawPath(caminho, paint);
}
}
This might help you,
I wrote an extensive answer for someone who had the same issue as you.
Check it out, hope it would help you too
https://stackoverflow.com/a/11357351/975959
So I have a MapView with a lot of markers, most of which are concentrated in mile wide clusters. When zoomed the markers overlap and appear to only be one. What I want to achieve is at a certain zoom level replace the overlapping markers with a group marker that will display the density of markers and onClick will zoom to display all markers inside. I know I can do this with brute force distance measurements but there must be a more efficient way. Anyone have any solution or smart algorithms on how I can achieve this?
Um... assuming the markers are not grouped, layered or anything: why - before showing them - don't you create a grid of certain density and simply bin the markers into the cells of your grid?
If you then count that several markers fall into the same bin (grid cell) - you can group them. If you need slightly more clever grouping, you might also check the neighbouring cells.
Maybe it sounds a bit primitive but:
No n^2 algorithms
No assumption about ordering of the input
No need to additionally process markers which are not going to be shown
The code for the grid:
Note - I come from the C++ world (got here through [algorithm] tag) so I'll stick to the pseudo-C++. I do not know the API of the mapview. But I would be surprised if this couldn't be efficiently translated into whatever language/library you are using.
Input:
- list of markers
- the rectangle viewing window in world coordinates (section of world we are currently looking at)
In the simplest form, it would look something like this:
void draw(MarkerList mlist, View v) {
//binning:
list<Marker> grid[densityX][densityY]; //2D array with some configurable, fixed density
foreach(Marker m in mlist) {
if (m.within(v)) {
int2 binIdx;
binIdx.x=floor(densityX*(m.coord.x-v.x1)/(v.x2-v.x1));
binIdx.y=floor(densityY*(m.coord.y-v.y1)/(v.y2-v.y1));
grid[binIdx.x][binIdx.y].push(m); //just push the reference
}
//drawing:
for (int i=0; i<densityX; ++i)
for (int j=0; j<densityY; ++j) {
if (grid[i][j].size()>N) {
GroupMarker g;
g.add(grid[i][j]); //process the list of markers belonging to this cell
g.draw();
} else {
foreach (Marker m in grid[i][j])
m.draw()
}
}
}
The problem that might appear is that an unwanted grid split may appear within some clustered group, forming two GroupMarkers. To counter that, you may want to consider not just one grid cell, but also its neighbors in the "\drawing" section, and - if grouped - mark neighboring cells as visited.
The following pragmatic solution based on pixel distance really worked best for me:
http://www.appelsiini.net/2008/11/introduction-to-marker-clustering-with-google-maps
I converted Cygnus X1's answer to Java. Put this method in your custom Overlay and modify drawSingle() and drawGroup() to suit your needs. You improve performance too, like converting the ArrayLists to primitive arrays.
#Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
// binning:
int densityX = 10;
int densityY = 10;
// 2D array with some configurable, fixed density
List<List<List<OverlayItem>>> grid = new ArrayList<List<List<OverlayItem>>>(
densityX);
for(int i = 0; i<densityX; i++){
ArrayList<List<OverlayItem>> column = new ArrayList<List<OverlayItem>>(densityY);
for(int j = 0; j < densityY; j++){
column.add(new ArrayList<OverlayItem>());
}
grid.add(column);
}
for (OverlayItem m : mOverlays) {
int binX;
int binY;
Projection proj = mapView.getProjection();
Point p = proj.toPixels(m.getPoint(), null);
if (isWithin(p, mapView)) {
double fractionX = ((double)p.x / (double)mapView.getWidth());
binX = (int) (Math.floor(densityX * fractionX));
double fractionY = ((double)p.y / (double)mapView.getHeight());
binY = (int) (Math
.floor(densityX * fractionY));
// Log.w("PointClusterer absolute", p.x+ ", "+p.y);
// Log.w("PointClusterer relative", fractionX+ ", "+fractionY);
// Log.w("PointClusterer portion", "Marker is in portion: " + binX
// + ", " + binY);
grid.get(binX).get(binY).add(m); // just push the reference
}
}
// drawing:
for (int i = 0; i < densityX; i++) {
for (int j = 0; j < densityY; j++) {
List<OverlayItem> markerList = grid.get(i).get(j);
if (markerList.size() > 1) {
drawGroup(canvas, mapView, markerList);
} else {
// draw single marker
drawSingle(canvas, mapView, markerList);
}
}
}
}
private void drawGroup(Canvas canvas, MapView mapView,
List<OverlayItem> markerList) {
GeoPoint point = markerList.get(0).getPoint();
Point ptScreenCoord = new Point();
mapView.getProjection().toPixels(point, ptScreenCoord);
Paint paint = new Paint();
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(30);
paint.setAntiAlias(true);
paint.setARGB(150, 0, 0, 0);
// show text to the right of the icon
canvas.drawText("GROUP", ptScreenCoord.x, ptScreenCoord.y + 30, paint);
}
private void drawSingle(Canvas canvas, MapView mapView,
List<OverlayItem> markerList) {
for (OverlayItem item : markerList) {
GeoPoint point = item.getPoint();
Point ptScreenCoord = new Point();
mapView.getProjection().toPixels(point, ptScreenCoord);
Paint paint = new Paint();
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(30);
paint.setAntiAlias(true);
paint.setARGB(150, 0, 0, 0);
// show text to the right of the icon
canvas.drawText("SINGLE", ptScreenCoord.x, ptScreenCoord.y + 30,
paint);
}
}
public static boolean isWithin(Point p, MapView mapView) {
return (p.x > 0 & p.x < mapView.getWidth() & p.y > 0 & p.y < mapView
.getHeight());
}
}
Assuming your markers are grouped together in an ItemizedOverlay you could create a method which was called when the map was zoomed. This would compare pixel co-ordinates of each marker to see if they overlap and set a flag. Then in the draw method you could draw either the grouped marker or individuals;
Something like:
//this would need to be wired to be called when the mapview is zoomed
//it sets the drawgrouped flag if co-ordinates are close together
Boolean drawGrouped=false;
public void onMapZoom(MapView mapView){
//loop thru overlay items
Integer i,l=this.size();
OverlayItem item;
Integer deltaX=null,deltaY=null;
Projection proj = mapView.getProjection();
Point p=new Point();
Integer x=null,y=null;
Integer tolerance = 10; //if co-ordinates less than this draw grouped icon
for(i=0;i<l;i++){
//get the item
item=this.getItem(i);
//convert the overlays position to pixels
proj.toPixels(item.getPoint(), p);
proj.toPixels(item.getPoint(), p);
//compare co-ordinates
if(i==0){
x=p.x;
y=p.y;
continue;
}
deltaX=Math.abs(p.x-x);
deltaY=Math.abs(p.y-y);
//if the co-ordinates are too far apart dont draw grouped
if(deltaX>tolerance || deltaY>tolerance){
drawGrouped=false;
return;
}
x=p.x;
y=p.y;
}
//all co-ords are within the tolerance
drawGrouped=true;
}
public void draw(android.graphics.Canvas canvas, MapView mapView, boolean shadow){
if(drawGrouped==true){
//draw the grouped icon *needs to be optimised to only do it once
drawGrouped(canvas,mapView,shadow);
return;
}
//not grouped do regular drawing
super.draw(canvas, mapView, shadow);
}
What you are looking for is usually called clustering. There are common techniques to do this, you can refer, for example, to this SO question, it leads to this post.
The basic idea is to divide the map on squares based on the current zoom level (you can cache calculations based on the zoom level to avoid recalculation when the user starts zooming), and to group them based which square they belong to. So you end up having some sort of grouping based on zoom level, ie for level 1-5 just draw the markers, for level 5-8 group them in squares of 20 miles, for 9-10 in squares of 50 miles, and so on.
Here is another relevant question on SO that you may want to take a look, not sure about the performance of this though: Android Maps Point Clustering
If your markers are grouped, you'll have a fair idea at what zoom level you should be displaying individual markers or the group marker e.g. zoom level > 17 then display individual markers, otherwise display the group marker. I used code something like this in my ItemizedOverlay to change my markers:
#Override
public void draw(Canvas canvas, MapView mapv, boolean shadow)
{
int zoom = mapv.getZoomLevel();
switch(zoom)
{
case 19:
setMarkersForZoomLevel19();
break;
case 18:
setMarkersForZoomLevel18();
break;
case 17:
setMarkersForZoomLevel17();
break;
case 16:
setMarkersForZoomLevel16();
break;
default:
// Hide the markers or remove the overlay from the map view.
mapv.getOverlays().clear();
}
area.drawArea(canvas, mapv);
// Putting this call here rather than at the beginning, ensures that
// the Overlay items are drawn over the top of canvas stuff e.g. route lines.
super.draw(canvas, mapv, false);
}
private void setMarkersForZoomLevel19()
{
for (JourneyOverlayItem item : mOverlays)
{
item.setMarker(areaPointIcon48);
}
}
If its possible to have the individual markers in a collection, you could easily get the largest and smallest latitude and longitude and the difference between them will give you the latitude and longitude span (this could then be used to zoom to the span to show the group of markers). Divide the spans by 2 and you should have the centre point for placing the group marker.
This is the approach that I used. However, it's O(n^2).
The pins must be sorted based on prominent.
Pick pin with the highest prominent. Look at all pins around it. Absorb pins near that pin.
Then move on to the next highest prominent pin. Do the same. Repeat.
Simple.
Things get complicated if you move the map around, zooming in, zooming out, and you want to ensure that the new pins are not being redrawn. So you check each cluster if they have to split during zoom in, and then you check each cluster if they have to merge during zoom out. Then you remove pins that's gone and add new pins. For every pin you add, you check whether they should join a cluster or form their own cluster.
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.