I need to draw shapes just like those you would create with custom maps on Google Maps, using Android\'s MapView.
In other words, if I draw a shape on t
I had the same problem myself, here's a full solution with demo locations:
import java.util.*;
import android.graphics.*;
import android.graphics.Paint.Style;
import android.graphics.Region.Op;
import android.os.Bundle;
import com.google.android.maps.*;
public class ShapeOverlayTest extends MapActivity {
private MapView m_map;
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
m_map = (MapView) findViewById(R.id.mapview);
m_map.displayZoomControls(true);
m_map.setBuiltInZoomControls(true);
}
@Override
protected void onStart() {
super.onStart();
Loc[][] areas = {
{
new Loc(51.51695436113811, -0.28686325139653757),
new Loc(51.5268179962453, -0.28118722558738923),
new Loc(51.526498459594215, -0.27779666308279755),
new Loc(51.52521530775356, -0.26943974607777654),
new Loc(51.52292555645698, -0.25813738590178786),
new Loc(51.52054465991048, -0.2498381618983569),
new Loc(51.51012230470141, -0.24509233633017083),
new Loc(51.50884762913046, -0.24465130560570497),
new Loc(51.50732063336974, -0.2441767643132881),
new Loc(51.50431624597833, -0.24473900326760137),
new Loc(51.49756328092904, -0.2714528238165076),
new Loc(51.50092541797557, -0.28360267232347336),
new Loc(51.50205958485736, -0.28490018935582045),
new Loc(51.50488447379555, -0.28681164237730944)
},
{
new Loc(51.50368617913765, -0.25313579464343156),
new Loc(51.51978611305675, -0.24842567405905958),
new Loc(51.51039382684418, -0.24460628015366626),
new Loc(51.508792552597576, -0.24397604687682156),
new Loc(51.50713008309719, -0.24346350415674722),
new Loc(51.502411013302684, -0.2508501075008919),
new Loc(51.502377240039664, -0.25160073203846817),
new Loc(51.50274364303565, -0.25204783703705536)
},
{
new Loc(51.49924084955314, -0.2858705706471945),
new Loc(51.50212820259818, -0.2791479893522646),
new Loc(51.49724510427319, -0.27427453152961206),
new Loc(51.49429724502515, -0.2799184038304611),
new Loc(51.494270969987404, -0.28180678948730314)
}
};
String[] areaNames = { "W3 Ealing", "W3 Hammersmith & Fulham", "W3 Hounslow" };
// for (Map.Entry> area : m_areas.entrySet()) {
// // to have much less points and make sure they are in order
// // the demo data already has these properties
// // http://en.wikipedia.org/wiki/Gift_wrapping_algorithm#Pseudocode
// area.setValue(Algo.convexHull(area.getValue()));
// }
Map> areaMap = new HashMap>();
for (int i = 0; i < areaNames.length; i++) {
List points = new ArrayList();
for (int j = 0; j < areas[i].length; j++) {
points.add(areas[i][j].toGeoPoint());
}
areaMap.put(areaNames[i], points);
}
m_map.getOverlays().add(new AreasOverlay(areaMap));
// TODO determine location better, e.g. averaging area points
GeoPoint center = new GeoPoint(51509704, -270710);
m_map.getController().setCenter(center);
m_map.getController().setZoom(15);
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
static class Loc {
private double m_lat;
private double m_lon;
public Loc(final double lat, final double lon) {
m_lat = lat;
m_lon = lon;
}
public GeoPoint toGeoPoint() {
return new GeoPoint((int) (m_lat * 1e6), (int) (m_lon * 1e6));
}
};
static class AreasOverlay extends Overlay {
private final Map> m_areas;
private final Paint m_paintFill;
private final Paint m_paintStroke;
private static final int ALPHA = 0x30ffffff; // 48 out of 255 transparent
private static final int[] COLORS =
{ Color.YELLOW, Color.MAGENTA, Color.CYAN, Color.RED, Color.GREEN, Color.BLUE };
static {
for (int i = 0; i < AreasOverlay.COLORS.length; i++) {
AreasOverlay.COLORS[i] &= AreasOverlay.ALPHA;
}
}
public AreasOverlay(final Map> areaMap) {
m_areas = areaMap;
// prepare paints
m_paintFill = new Paint();
m_paintFill.setStyle(Paint.Style.FILL);
m_paintStroke = new Paint(Paint.ANTI_ALIAS_FLAG);
m_paintStroke.setStyle(Style.STROKE);
m_paintStroke.setAntiAlias(true);
m_paintStroke.setStrokeWidth(3);
}
@Override
public void draw(final Canvas canvas, final MapView mapView, final boolean shadow) {
super.draw(canvas, mapView, shadow);
if (shadow) {
return;
}
Projection projection = mapView.getProjection();
List areaPaths = getPaths(projection, m_areas);
drawPaths(canvas, areaPaths);
}
private List getPaths(final Projection projection, final Map> areas) {
List areaPaths = new ArrayList(areas.size());
for (Map.Entry> entry : areas.entrySet()) {
List sourceList = entry.getValue();
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
Iterator it = sourceList.iterator();
Point point = nextDrawPoint(projection, it);
path.moveTo(point.x, point.y);
while (it.hasNext()) {
point = nextDrawPoint(projection, it);
path.lineTo(point.x, point.y);
}
path.close();
areaPaths.add(path);
}
return areaPaths;
}
/**
*
* - Draw with different colors.
*
- Draw strokes first for them to be always visible.
*
- Draw fills next with each removing from the drawable area.
*
*/
private void drawPaths(final Canvas canvas, final List areaPaths) {
int currentColorIndex;
currentColorIndex = 0;
for (Path path : areaPaths) {
int currentColor = AreasOverlay.COLORS[currentColorIndex++];
currentColorIndex %= AreasOverlay.COLORS.length;
m_paintStroke.setColor(currentColor & 0xff7f7f7f); // make it darker by clearing the high bit
canvas.drawPath(path, m_paintStroke);
}
currentColorIndex = 0;
for (Path path : areaPaths) {
int currentColor = AreasOverlay.COLORS[currentColorIndex++];
currentColorIndex %= AreasOverlay.COLORS.length;
m_paintFill.setColor(currentColor);
canvas.drawPath(path, m_paintFill);
canvas.clipPath(path, Op.DIFFERENCE); // don't allow to draw over each other
}
}
private Point nextDrawPoint(final Projection projection, final Iterator it) {
GeoPoint geo = it.next();
Point p = new Point();
projection.toPixels(geo, p);
return p;
}
}
}