Android Google Maps API v2在MapFragment上绘制圆的一部分
我需要画一些像这样的东西,它会被画出来,透明度很低 此外,它还需要可点击(点击事件等) 我知道,在API v1中,您必须使用覆盖,并使用画布和一些数学来扩展它。 在谷歌地图API v2中,最简单的方法是什么 PS:半径是可变的 (供进一步参考) 编辑1: 我实现了CanvasTileProvider子类并重写了它的onDraw()方法: 此外,我还从MapActivity中添加了以下内容:Android Google Maps API v2在MapFragment上绘制圆的一部分,android,google-maps-android-api-2,Android,Google Maps Android Api 2,我需要画一些像这样的东西,它会被画出来,透明度很低 此外,它还需要可点击(点击事件等) 我知道,在API v1中,您必须使用覆盖,并使用画布和一些数学来扩展它。 在谷歌地图API v2中,最简单的方法是什么 PS:半径是可变的 (供进一步参考) 编辑1: 我实现了CanvasTileProvider子类并重写了它的onDraw()方法: 此外,我还从MapActivity中添加了以下内容: private void loadSegmentTiles() { TileProvider
private void loadSegmentTiles() {
TileProvider tileProvider;
TileOverlay tileOverlay = mMap.addTileOverlay(
new TileOverlayOptions().tileProvider(new SegmentTileProvider(new LatLng(45.00000,15.000000), 250, 30)));
}
现在我想知道为什么我的弧不在地图上?创建一个视图,覆盖其onDraw方法以在画布上使用drawArc,并将其添加到MapFragment中。可以在drawArc中指定半径。在视图上设置onClickListener(或者onTouch,任何可以用于普通视图的侦听器)。对于绘制圆线段,如果线段主要是静态的,我会注册一个TileProvider。(平铺通常只加载一次,然后缓存。)要检查单击事件,可以注册onmaClickListener并在片段上循环,以检查单击的LatLng是否在其中一个片段内。(详见下文。) 下面是一个TileProvider示例,您可以对其进行子类化,只需实现onDraw方法。
一个重要的注意事项:子类必须是线程安全的!onDraw方法将由多个线程同时调用。因此,避免在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;
}
}
}
/*导入应该是显而易见的*/
公共抽象类CanvasTileProvider实现TileProvider{
专用静态int TILE_SIZE=256;
私有位图线程本地TL位图;
@抑制警告(“未使用”)
private static final String TAG=CanvasTileProvider.class.getSimpleName();
公共CanvasTileProvider(){
超级();
tlBitmap=新的BitMapThreadLocal();
}
@凌驾
//警告:必须是线程安全的。要避免创建大量位图,
//我使用ThreadLocal的一个子类!!!
公共平铺getTile(整数x、整数y、整数缩放){
瓷砖投影=新瓷砖投影(瓷砖尺寸,
x、 y,缩放);
字节[]数据;
位图图像=getNewBitmap();
画布=新画布(图像);
onDraw(画布、投影);
数据=位图字节数组(图像);
瓷砖=新瓷砖(瓷砖尺寸、瓷砖尺寸、数据);
返回瓷砖;
}
/**必须由具体的TileProvider实现*/
抽象void onDraw(画布、瓷砖投影);
/**
*获取一个空位图,但是可以从中以前的调用中重用该位图
*同一根线。
*
*@返回
*/
私有位图getNewBitmap(){
位图Bitmap=tlBitmap.get();
//清除上一个位图
位图。擦除颜色(颜色。透明);
返回位图;
}
专用静态字节[]位图字节数组(位图bm){
ByteArrayOutputStream bos=新建ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG,100,bos);
字节[]数据=bos.toByteArray();
返回数据;
}
类BitMapThreadLocal扩展ThreadLocal{
@凌驾
受保护位图初始值(){
位图图像=位图。创建位图(平铺大小、平铺大小、,
配置ARGB_8888);
返回图像;
}
}
}
使用传递到onDraw方法的投影,首先获取瓷砖的边界。如果边界内没有线段,只需返回。否则,将您的序列绘制到画布中。方法projection.latLngToPoint可帮助您将LatLng转换为画布的像素
/** 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;
}
}
}
/**在板条坐标和平铺内的像素之间进行转换*/
公共类TileProjection{
私人INTX;
私营企业;
私有int-zoom;
私人室内瓷砖尺寸;
私人双点像素;
私人双像素像素像素;
私密双像素人眼弧度;
TILEPROJECT(整数tileSize、整数x、整数y、整数缩放){
this.TILE_SIZE=瓷砖大小;
这个.x=x;
这个。y=y;
this.zoom=zoom;
pixelOrigin=新的双点(平铺尺寸/2,平铺尺寸/2);
pixelsPerLonDegree=瓷砖大小/360d;
pixelsPerLonRadian_u=瓷砖大小/(2*Math.PI);
}
/**以LatLng坐标获取瓷砖的尺寸*/
公共LatLngBounds getTileBounds(){
DoublePoint tileSW=新的DoublePoint(x*瓷砖大小,(y+1)*瓷砖大小);
DoublePoint worldSW=像素到世界坐标(tileSW);
LatLng SW=世界合作组织(worldSW);
DoublePoint tileNE=新的DoublePoint((x+1)*瓷砖大小,y*瓷砖大小);
双点世界坐标=像素世界坐标(tileNE);
LatLng NE=世界合作组织(worldNE);
返回新的LatLngBounds(西南、东北);
}
/**
*计算平铺内相对于左上角的像素坐标
*瓷砖的角(原点)。
*/
公共无效latLngToPoint(LatLng LatLng,双点结果){
LatlngToWorld坐标(latLng,结果);
世界像素坐标(结果,结果);
结果x-=x*瓷砖尺寸;
结果y-=y*瓷砖尺寸;
}
专用双点像素世界坐标(双点像素坐标){
int numTiles=1点(纬度){
/*多边形与光线的水平面相交*/
如果(点经度=vj经度){
/*
*边的起点和终点在点的左侧。否
*射线交叉。
*/
}否则{
双交叉经度=(vj.经度-vi.经度)
*(点纬度-六纬度)
/(vj.纬度-vi.纬度)+vi.经度;
if(点经度<交叉经度){
内部=!内部;
}
}
}
}
返回内部;
}
如您所见,我有一个非常类似的任务要解决:-)这将起作用,但是当地图移动时,弧不会随之移动,您可以随时跟踪地图移动并根据需要转换视图。太好了。。。我会试试你的建议。。。它不应该动。是的
/** 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;
}
}
}
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;
}