我正在使用叠加来标记Google地图上的区域,方法是绘制我从任何来源获得的数万个GeoPoints的形状。这有效,看起来像这样:
@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);
}
问题是这非常耗费资源。每次在MapView上滚动或移动地图时,都必须一遍又一遍地计算路径,因为像素坐标已经改变。绘制的区域可能变得如此之大,以至于MapView上的滚动速度太慢而无法使用。
我的想法是
我不确定是否有更好的方法。
我有什么想法可以解决这个问题? (我使用的是Google Maps API 1,无法更改。)
答案 0 :(得分:2)
在尝试弄清楚如何匹配地图的移动之前,对当前代码进行一些优化可能会带来显着的节省。特别是,内部循环中的这两行执行次数最多,但执行起来相当昂贵(两次内存分配,浮点乘法和四次方法调用)。
Point p = new Point();
projection.toPixels(new GeoPoint((int)(coordinates.get(i).getLatitude() * 1E6), (int)(coordinates.get(i).getLongitude() * 1E6)), p);
首先,您只需要一个Point
对象,因此请避免在循环中分配它。将其移至path.rewind();
其次,如果您将坐标预先计算为GeoPoints
而不是每次都计算它们,那么您将在draw
例程中节省大量处理。您还可以通过一些工作摆脱if
语句。假设您将coordinate
的列表预转换为GeoPoint
列表,并通过polygon.getGeoCoordinates()
提供,则最终可能会使您的内部循环看起来像 -
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);
}
}
这比你以前做的要快得多。
答案 1 :(得分:1)
在最后几天摆弄后我找到了一个可能的解决方案(我认为没有更好的解决方案)不能一遍又一遍地画出路径,而是将其移动到当前位置。
困难的部分是弄清楚如何缓存绘制的形状,而不是一遍又一遍地计算它。这可以通过使用Matrix来完成。有了这个矩阵(我想这可以作为某种“模板”),你可以操纵路径内的点坐标。第一次(当有人开始移动地图时)我像往常一样绘制区域。当它试图第二次或更多次计算时,我不重绘形状,但我通过计算从当前点到最后一点的“delta”来操纵路径。我知道目前的观点是什么,因为我总是将原始GeoPoint(始终保持不变)映射到当前投影产生的点。 “delta”需要设置为Matrix。之后,我使用这个新的矩阵转换路径。结果非常快。滚动地图的速度与不使用叠加层一样快。
这看起来像这样(这不是生产代码,它还不能处理缩放,但它显示了我用作优化基础的原则):
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;
}
}