8

我正在使用 Overlay 来标记 Google Maps 上的区域,方法是绘制我从任何来源获得的一万个 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 上的滚动非常缓慢,以至于无法正常使用。

我的想法是

  • 以某种方式缓存路径生成的“形状”,并在 MapView 上的缩放级别更改时重新绘制它。
  • 以某种方式在“动态”上绘制绘画 - 位图将其用作叠加层(可能作为 ItemizedOverlay),侦听 MapView 滚动并将位图移动滚动距离。

我不确定是否有更好的方法。

有什么想法可以解决这个问题吗?(我正在使用 Google Maps API 1 并且无法更改)。

4

2 回答 2

4

在尝试弄清楚如何匹配地图的移动之前,对您当前的代码进行一些优化可能会节省大量资金。特别是,内部循环中的这两行执行次数最多,但执行起来相当昂贵(两次内存分配、浮点乘法和四个方法调用)。

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);
     }
}

这将比你以前做的要快得多。

于 2013-01-31T01:09:14.620 回答
1

在最后几天修补之后,我找到了一个可能的解决方案(我认为没有更好的解决方案)不要一遍又一遍地绘制路径,而是将其移动到当前位置。

困难的部分是弄清楚如何缓存绘制的形状而不是一遍又一遍地计算它。这可以通过使用矩阵来完成。使用这个矩阵(我认为这是某种“模板”),您可以操纵路径内的点坐标。第一次(当有人开始移动地图时)我像往常一样绘制该区域。当它尝试第二次或更多次计算时,我不会重新绘制形状,而是通过计算从当前点到最后一点的“增量”来操纵路径。我知道当前点是什么,因为我总是将原始 GeoPoint(始终保持不变)映射到当前投影产生的点。“增量”需要设置为矩阵。之后,我使用这个新矩阵来转换路径。结果真的很快。

这看起来像这样(这不是生产代码,它还不能处理缩放,但它显示了我用作优化基础的原理):

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;
    }
}
于 2013-02-03T15:35:46.280 回答