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 the map, when I zoom out it should shrink, covering the same area of the map regardless of the zoom level.
Example following the Android Reference:
@Override
public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when)
{
super.draw(canvas, mapView, shadow);
//---translate the GeoPoint to screen pixels---
Point screenPts = new Point();
mapView.getProjection().toPixels(p, screenPts);
Paint boxPaint = new Paint();
boxPaint.setColor(android.graphics.Color.WHITE);
boxPaint.setStyle(Paint.Style.FILL);
boxPaint.setAlpha(140);
canvas.drawCircle(screenPts.x, screenPts.y, 20, boxPaint);
return true;
}
This shows a white circle on the map, but if you zoom out, the circle is the same size. Perhaps using canvas is not right approach?
I need something like how Google Maps highlights neighborhoods or cities:
Any ideas? Thanks in advance!
The Maps API provides built-in zoom controls that appear in the bottom right hand corner of the map. These are disabled by default, but can be enabled by calling UiSettings. setZoomControlsEnabled(true) .
The Google Maps API provides map tiles at various zoom levels for map type imagery. Most roadmap imagery is available from zoom levels 0 to 18, for example. Satellite imagery varies more widely as this imagery is not generated, but directly photographed.
Double tap a spot on the map, and then: Drag down to zoom in. Drag up to zoom out.
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<String, List<Loc>> 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<String, List<GeoPoint>> areaMap = new HashMap<String, List<GeoPoint>>();
for (int i = 0; i < areaNames.length; i++) {
List<GeoPoint> points = new ArrayList<GeoPoint>();
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<String, List<GeoPoint>> 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<String, List<GeoPoint>> 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<Path> areaPaths = getPaths(projection, m_areas);
drawPaths(canvas, areaPaths);
}
private List<Path> getPaths(final Projection projection, final Map<String, List<GeoPoint>> areas) {
List<Path> areaPaths = new ArrayList<Path>(areas.size());
for (Map.Entry<String, List<GeoPoint>> entry : areas.entrySet()) {
List<GeoPoint> sourceList = entry.getValue();
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
Iterator<GeoPoint> 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;
}
/**
* <ul>
* <li>Draw with different colors.
* <li>Draw strokes first for them to be always visible.
* <li>Draw fills next with each removing from the drawable area.
* </ul>
*/
private void drawPaths(final Canvas canvas, final List<Path> 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<GeoPoint> it) {
GeoPoint geo = it.next();
Point p = new Point();
projection.toPixels(geo, p);
return p;
}
}
}
The radius for drawCircle is in pixels so it make sense that the circle is always the same size. You have to scale the radius based on the zoom level. The example below will graph a geometry from the JTS Topology Suite that will scale.
public class MapOverlay extends Overlay {
private static final String TAG = MapOverlay.class.getName();
// Allocate once and reuse
private final Paint mPaint = new Paint();
private final Path mPath = new Path();
// Region to highlight
private Geometry mGeometry;
/**
* @param geometry Region to highlight on map
*/
public MapOverlay(Geometry geometry) {
// Set region
mGeometry = geometry;
// Edit paint style
mPaint.setDither(true);
mPaint.setColor(Color.rgb(128, 136, 231));
mPaint.setAlpha(100);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(6);
}
/**
* Draw the overlay over the map.
*
* @see com.google.android.maps.Overlay#draw(Canvas, MapView, boolean)
*/
@Override
public void draw(Canvas canvas, MapView mapv, boolean shadow) {
super.draw(canvas, mapv, shadow);
if (mGeometry != null) {
// TODO There could be more than one geometries
Geometry g = mGeometry.getGeometryN(0);
final Point p = new Point();
boolean first = true;
mPath.reset();
for (Coordinate c : g.getCoordinates()) {
// Convert lat/lon to pixels on screen
// GeoPoint is immutable so allocation is unavoidable
Projection projection = mapv.getProjection();
projection.toPixels(new GeoPoint((int) (c.y * 1E6), (int) (c.x * 1E6)), p);
// Set path starting point to first coordinate
// otherwise default start is (0,0)
if (first) {
mPath.moveTo(p.x, p.y);
first = false;
}
// Add new point to path
mPath.lineTo(p.x, p.y);
}
}
// Draw the path with give paint
canvas.drawPath(mPath, mPaint);
}
Adapted from here: MapSelectionOverlay.java
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With