谷歌地图圆上MapFragment API第2战平部分 [英] Google Maps API v2 draw part of circle on MapFragment

查看:201
本文介绍了谷歌地图圆上MapFragment API第2战平部分的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要画这样的事情,这将画并没有什么透明度

I need to draw something like this which will be painted and have little transparency

此外,它必须是可点击(onTouch事件等)

Also it needs to be clickable (onTouch event etc)

我知道,在API V1,你必须使用覆盖和使用帆布和一些数学扩展它。 什么是最容易做到这一点在谷歌地图API V2的方式?

I know that in API v1 you have to use Overlay and extend it using canvas and some mathematics. What is easiest way to do it in Google Map API v2?

PS:半径是可变的。

PS: Radius is variable.

(更多参考) 编辑1:

(For further reference) EDIT 1:

我实现CanvasTileProvider子类,并覆盖其的OnDraw()方法:

I implemented CanvasTileProvider subclass and override its onDraw() method:

@Override
void onDraw(Canvas canvas, TileProjection projection) {
    // TODO Auto-generated method stub

    LatLng tempLocation = moveByDistance(mSegmentLocation, mSegmentRadius, mSegmentAngle);

    DoublePoint segmentLocationPoint = new DoublePoint(0, 0);
    DoublePoint tempLocationPoint = new DoublePoint(0, 0);

    projection.latLngToPoint(mSegmentLocation, segmentLocationPoint);
    projection.latLngToPoint(tempLocationPoint, tempLocationPoint);

    float radiusInPoints = FloatMath.sqrt((float) (Math.pow(
            (segmentLocationPoint.x - tempLocationPoint.x), 2) + Math.pow(
            (segmentLocationPoint.y - tempLocationPoint.y), 2)));

    RectF segmentArea = new RectF();
    segmentArea.set((float)segmentLocationPoint.x - radiusInPoints, (float)segmentLocationPoint.y - radiusInPoints, 
            (float)segmentLocationPoint.x + radiusInPoints, (float)segmentLocationPoint.y + radiusInPoints);

    canvas.drawArc(segmentArea, getAdjustedAngle(mSegmentAngle), 
            getAdjustedAngle(mSegmentAngle + 60), true, getOuterCirclePaint());


}

另外,我添加了这个来自MapActivity:

Also, I added this from MapActivity:

private void loadSegmentTiles() {

     TileProvider tileProvider; 
     TileOverlay tileOverlay = mMap.addTileOverlay(
         new TileOverlayOptions().tileProvider(new SegmentTileProvider(new LatLng(45.00000,15.000000), 250, 30)));

}

现在我不知道为什么我的弧是不是在地图上?

Now I'm wondering why my arc isn't on map?

推荐答案

有关绘制圆段,我会注册一个TileProvider,如果段主要是静态的。 (瓷砖通常只加载一次,然后缓存)。对于检查点击事件,你可以注册一个onMapClickListener和循环在你的段检查点击的经纬度是否在你的领域之一。 (请参阅下面的更多细节。)

For drawing the circle segments, I would register a TileProvider, if the segments are mainly static. (Tiles are typically loaded only once and then cached.) For checking for click events, you can register an onMapClickListener and loop over your segments to check whether the clicked LatLng is inside one of your segments. (see below for more details.)

下面是一个TileProvider例如,你可以将子类,只是实现的OnDraw方法。
重要提示:的子类必须是线程安全的!该OnDraw的方法将同时被多个线程调用。所以要避免这些内部的OnDraw改变任何全局变量!

Here is a TileProvider example, which you could subclass and just implement the onDraw method.
One important note: The subclass must be thread safe! The onDraw method will be called by multiple threads simultaneously. So avoid any globals which are changed inside onDraw!

/* imports should be obvious */ 
public abstract class CanvasTileProvider implements TileProvider {
private static int TILE_SIZE = 256;

private BitMapThreadLocal tlBitmap;

@SuppressWarnings("unused")
private static final String TAG = CanvasTileProvider.class.getSimpleName();

public CanvasTileProvider() {
    super();
    tlBitmap = new BitMapThreadLocal();
}

@Override
// Warning: Must be threadsafe. To still avoid creation of lot of bitmaps,
// I use a subclass of ThreadLocal !!!
public Tile getTile(int x, int y, int zoom) {
    TileProjection projection = new TileProjection(TILE_SIZE,
            x, y, zoom);

    byte[] data;
    Bitmap image = getNewBitmap();
    Canvas canvas = new Canvas(image);
    onDraw(canvas, projection);
    data = bitmapToByteArray(image);
    Tile tile = new Tile(TILE_SIZE, TILE_SIZE, data);
    return tile;
}

/** Must be implemented by a concrete TileProvider */
abstract void onDraw(Canvas canvas, TileProjection projection);

/**
 * Get an empty bitmap, which may however be reused from a previous call in
 * the same thread.
 * 
 * @return
 */
private Bitmap getNewBitmap() {
    Bitmap bitmap = tlBitmap.get();
    // Clear the previous bitmap
    bitmap.eraseColor(Color.TRANSPARENT);
    return bitmap;
}

private static byte[] bitmapToByteArray(Bitmap bm) {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    bm.compress(Bitmap.CompressFormat.PNG, 100, bos);
    byte[] data = bos.toByteArray();
    return data;
}

class BitMapThreadLocal extends ThreadLocal<Bitmap> {
    @Override
    protected Bitmap initialValue() {
        Bitmap image = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE,
                Config.ARGB_8888);
        return image;
    }
}
}

使用突起,其被传递到OnDraw中的方法,以获得在所述砖的第一边界。如果没有段的范围内,只是返回。否则,请你seqment到画布上。该方法projection.latLngToPoint帮助您从经纬度转换为画布的像素。

Use the projection, which is passed into the onDraw method, to get at first the bounds of the tile. If no segment is inside the bounds, just return. Otherwise draw your seqment into the canvas. The method projection.latLngToPoint helps you to convert from LatLng to the pixels of the canvas.

/** Converts between LatLng coordinates and the pixels inside a tile. */
public class TileProjection {

private int x;
private int y;
private int zoom;
private int TILE_SIZE;

private DoublePoint pixelOrigin_;
private double pixelsPerLonDegree_;
private double pixelsPerLonRadian_;

TileProjection(int tileSize, int x, int y, int zoom) {
    this.TILE_SIZE = tileSize;
    this.x = x;
    this.y = y;
    this.zoom = zoom;
    pixelOrigin_ = new DoublePoint(TILE_SIZE / 2, TILE_SIZE / 2);
    pixelsPerLonDegree_ = TILE_SIZE / 360d;
    pixelsPerLonRadian_ = TILE_SIZE / (2 * Math.PI);
}

/** Get the dimensions of the Tile in LatLng coordinates */
public LatLngBounds getTileBounds() {
    DoublePoint tileSW = new DoublePoint(x * TILE_SIZE, (y + 1) * TILE_SIZE);
    DoublePoint worldSW = pixelToWorldCoordinates(tileSW);
    LatLng SW = worldCoordToLatLng(worldSW);
    DoublePoint tileNE = new DoublePoint((x + 1) * TILE_SIZE, y * TILE_SIZE);
    DoublePoint worldNE = pixelToWorldCoordinates(tileNE);
    LatLng NE = worldCoordToLatLng(worldNE);
    return new LatLngBounds(SW, NE);
}

/**
 * Calculate the pixel coordinates inside a tile, relative to the left upper
 * corner (origin) of the tile.
 */
public void latLngToPoint(LatLng latLng, DoublePoint result) {
    latLngToWorldCoordinates(latLng, result);
    worldToPixelCoordinates(result, result);
    result.x -= x * TILE_SIZE;
    result.y -= y * TILE_SIZE;
}


private DoublePoint pixelToWorldCoordinates(DoublePoint pixelCoord) {
    int numTiles = 1 << zoom;
    DoublePoint worldCoordinate = new DoublePoint(pixelCoord.x / numTiles,
            pixelCoord.y / numTiles);
    return worldCoordinate;
}

/**
 * Transform the world coordinates into pixel-coordinates relative to the
 * whole tile-area. (i.e. the coordinate system that spans all tiles.)
 * 
 * 
 * Takes the resulting point as parameter, to avoid creation of new objects.
 */
private void worldToPixelCoordinates(DoublePoint worldCoord, DoublePoint result) {
    int numTiles = 1 << zoom;
    result.x = worldCoord.x * numTiles;
    result.y = worldCoord.y * numTiles;
}

private LatLng worldCoordToLatLng(DoublePoint worldCoordinate) {
    DoublePoint origin = pixelOrigin_;
    double lng = (worldCoordinate.x - origin.x) / pixelsPerLonDegree_;
    double latRadians = (worldCoordinate.y - origin.y)
            / -pixelsPerLonRadian_;
    double lat = Math.toDegrees(2 * Math.atan(Math.exp(latRadians))
            - Math.PI / 2);
    return new LatLng(lat, lng);
}

/**
 * Get the coordinates in a system describing the whole globe in a
 * coordinate range from 0 to TILE_SIZE (type double).
 * 
 * Takes the resulting point as parameter, to avoid creation of new objects.
 */
private void latLngToWorldCoordinates(LatLng latLng, DoublePoint result) {
    DoublePoint origin = pixelOrigin_;

    result.x = origin.x + latLng.longitude * pixelsPerLonDegree_;

    // Truncating to 0.9999 effectively limits latitude to 89.189. This is
    // about a third of a tile past the edge of the world tile.
    double siny = bound(Math.sin(Math.toRadians(latLng.latitude)), -0.9999,
            0.9999);
    result.y = origin.y + 0.5 * Math.log((1 + siny) / (1 - siny))
            * -pixelsPerLonRadian_;
};

/** Return value reduced to min and max if outside one of these bounds. */
private double bound(double value, double min, double max) {
    value = Math.max(value, min);
    value = Math.min(value, max);
    return value;
}

/** A Point in an x/y coordinate system with coordinates of type double */
public static class DoublePoint {
    double x;
    double y;

    public DoublePoint(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

}

最后,你需要一些东西来检查,无论是在点击经纬度坐标是你的段内。 因此,我会用经纬度,坐标列表,在这里你的情况下,简单的三角形可能足以逼近段。对于经纬度的每个列表坐标,也就是每个部分,您就可以调用类似如下:

Finally you need something to check, whether a click on a LatLng-Coordinate is inside of your segment. I would therefore approximate the segment by a list of LatLng-Coordinates, where in your case a simple triangle may be sufficient. For each list of LatLng coordinates, i.e. for each segment, you may then call something like the following:

private static boolean isPointInsidePolygon(List<LatLng> vertices, LatLng point) {
    /**
     * Test is based on a horizontal ray, starting from point to the right.
     * If the ray is crossed by an even number of polygon-sides, the point
     * is inside the polygon, otherwise it is outside.
     */
    int i, j;
    boolean inside = false;
    int size = vertices.size();
    for (i = 0, j = size - 1; i < size; j = i++) {
        LatLng vi = vertices.get(i);
        LatLng vj = vertices.get(j);
        if ((vi.latitude > point.latitude) != (vj.latitude > point.latitude)) {
            /* The polygonside crosses the horizontal level of the ray. */
            if (point.longitude <= vi.longitude
                    && point.longitude <= vj.longitude) {
                /*
                 * Start and end of the side is right to the point. Side
                 * crosses the ray.
                 */
                inside = !inside;
            } else if (point.longitude >= vi.longitude
                    && point.longitude >= vj.longitude) {
                /*
                 * Start and end of the side is left of the point. No
                 * crossing of the ray.
                 */
            } else {
                double crossingLongitude = (vj.longitude - vi.longitude)
                        * (point.latitude - vi.latitude)
                        / (vj.latitude - vi.latitude) + vi.longitude;
                if (point.longitude < crossingLongitude) {
                    inside = !inside;
                }
            }
        }
    }
    return inside;
}

正如您可能会看到,我有一个非常类似的任务,解决:-)

As you may see, I had a very similar task to solve :-)

这篇关于谷歌地图圆上MapFragment API第2战平部分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆