我能够在 Google map 内绘制多边形。现在,当我完成在 map 上绘制该区域的多边形时,我想用相同的图标填充该多边形。我可以用颜色填充多边形,但我想用图标填充它,并在边界处添加一些填充,并在图标之间添加一些指定的空间。我该怎么做?
最佳答案
Google Maps Android SDK 没有“开箱即用”的解决方案 Polygon
使用图案填充(仅实心填充),但您可以使用一些解决方法。
TLDR;
假设您需要填充公园多边形:
final List<LatLng> polygonPoints = new ArrayList<>();
polygonPoints.add(new LatLng(50.444477, 30.498976));
polygonPoints.add(new LatLng(50.445290, 30.500864));
polygonPoints.add(new LatLng(50.443958, 30.508921));
polygonPoints.add(new LatLng(50.443247, 30.508642));
polygonPoints.add(new LatLng(50.442830, 30.508878));
polygonPoints.add(new LatLng(50.440965, 30.508256));
polygonPoints.add(new LatLng(50.441949, 30.501443));
final Polygon polygon = mGoogleMap.addPolygon(new PolygonOptions()
.addAll(polygonPoints)
.strokeColor(Color.BLUE)
.fillColor(Color.argb(20, 0, 255, 0)));
带有树木图标(R.drawable.ic_forest
):
您可以通过以下方式完成:
1) 通过带有自定义(树)图标的标记“填充”。为此,您需要找到多边形边界矩形并在循环中添加标记作为方形贪婪。要仅在多边形内部添加标记,您可以使用 PolyUtil.containsLocation()
来自Google Maps Android API Utility Library :
LatLngBounds bounds = getPolygonBounds(polygon.getPoints());
BitmapDescriptor icon = BitmapDescriptorFactory.fromResource(R.drawable.ic_forest);
double boundHeight = bounds.northeast.latitude - bounds.southwest.latitude;
double boundWidth = bounds.northeast.longitude - bounds.southwest.longitude;
double gridCntLat = 5; // 5 icons vertically
double gridCntLon = 5; // 5 icons horizontally
double dLat = (bounds.northeast.latitude - bounds.southwest.latitude) / gridCntLat;
double dLng = (bounds.northeast.longitude - bounds.southwest.longitude) / gridCntLon;
double lat = dLat + bounds.southwest.latitude;
while (lat < bounds.northeast.latitude) {
double lon = dLng + bounds.southwest.longitude;
while (lon < bounds.northeast.longitude) {
LatLng iconPos = new LatLng(lat, lon);
if (PolyUtil.containsLocation(iconPos, polygonPoints, true)) {
MarkerOptions markerOptions = new MarkerOptions().position(iconPos)
.draggable(false)
.flat(true)
.icon(icon);
mGoogleMap.addMarker(markerOptions);
}
lon += dLng;
}
lat += dLat;
}
其中 getPolygonBounds()
是:
private LatLngBounds getPolygonBounds(List<LatLng> polygon) {
LatLngBounds.Builder builder = new LatLngBounds.Builder();
for(int i = 0 ; i < polygon.size() ; i++) {
builder.include(polygon.get(i));
}
LatLngBounds bounds = builder.build();
return bounds;
}
和 ... 私有(private) GoogleMap mGoogleMap; ...
@Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
...
}
结果你得到了类似的东西:
这是最简单的解决方案,但有些图标可能部分位于多边形边框之外(您可以通过 MarkerOptions.anchor()
方法稍微调整它们,如果您希望标记图标随多边形旋转,也可以设置 MarkerOptions.flat(true)
)并且对于大量标记图开始滞后。
2) 使用“动态”(以编程方式为每个多边形创建)GroundOverlay
。为此,您还需要确定多边形矩形边界,然后创建具有相同像素比例的覆盖位图(还需要通过较大的边来限制它以避免内存不足(OOM)错误)。注意您需要使用Projection
计算矩形边界,因为不同纬度和经度的 LatLng 度数以米为单位的大小与以像素为单位的大小不同。
然后,您需要在创建的位图 Canvas 上绘制多边形。为此,您可以使用 Path
具有重新计算的 LatLng 多边形点到本地叠加位图坐标(以像素为单位)的对象。您应该使用简单的比例,而不是 Projection.toScreenLocation()
:
screenPoint.x = (int) (width * (latLng.longitude - bounds.southwest.longitude) / boundWidth);
screenPoint.y = height - (int) (height * (latLng.latitude - bounds.southwest.latitude) / boundHeight); // y-axis of screen is inverted to Lat
要使用图标的位图填充覆盖位图上的多边形,您可以使用 Shader
并将其设置为 Paint
对象 Paint.setShader()
。最后,您可以将创建的位图添加为 GoogleMap 对象上的地面叠加层。使用 tileBitmap
填充 polygon
的 fillPoly()
方法的完整源代码:
private GroundOverlay fillPoly(Projection projection, Polygon polygon, Bitmap tileBitmap, float transparency) {
LatLngBounds bounds = getPolygonBounds(polygon.getPoints());
double boundHeight = bounds.northeast.latitude - bounds.southwest.latitude;
double boundWidth = bounds.northeast.longitude - bounds.southwest.longitude;
Point northEast = projection.toScreenLocation(bounds.northeast);
Point southWest = projection.toScreenLocation(bounds.southwest);
int screenBoundHeight = northEast.y - southWest.y;
int screenBoundWidth = northEast.x - southWest.x;
// determine overlay bitmap size
double k = Math.abs((double) screenBoundHeight / (double) screenBoundWidth);
int overlayWidth;
int overlayHeight;
if (Math.abs(boundWidth) > Math.abs(boundHeight)) {
overlayWidth = OVERLAY_MAX_SIZE;
overlayHeight = (int) (overlayWidth * k);
} else {
overlayHeight = OVERLAY_MAX_SIZE;
overlayWidth = (int) (overlayHeight * k);
}
// create overlay bitmap
Bitmap overlayBitmap = createOverlayBitmap(overlayWidth, overlayHeight, bounds, polygon.getPoints(), tileBitmap);
// create ground overlay
GroundOverlayOptions overlayOptions = new GroundOverlayOptions()
.image(BitmapDescriptorFactory.fromBitmap(overlayBitmap))
.transparency(transparency)
.positionFromBounds(bounds);
return mGoogleMap.addGroundOverlay(overlayOptions);
}
其中 createOverlayBitmap()
是:
private Bitmap createOverlayBitmap(int width, int height, LatLngBounds bounds, List<LatLng> polygon, Bitmap tileBitmap) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Path path = new Path();
BitmapShader shader = new BitmapShader(tileBitmap,
Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(10);
paint.setAntiAlias(true);
paint.setShader(shader);
List<Point> screenPoints = new ArrayList<>(polygon.size());
double boundHeight = bounds.northeast.latitude - bounds.southwest.latitude;
double boundWidth = bounds.northeast.longitude - bounds.southwest.longitude;
for (int i = 0; i < polygon.size(); i++) {
LatLng latLng = polygon.get(i);
Point screenPoint = new Point();
screenPoint.x = (int) (width * (latLng.longitude - bounds.southwest.longitude) / boundWidth);
screenPoint.y = height - (int) (height * (latLng.latitude - bounds.southwest.latitude) / boundHeight);
screenPoints.add(screenPoint);
}
path.moveTo(screenPoints.get(0).x, screenPoints.get(0).y);
for (int i = 1; i < polygon.size(); i++) {
path.lineTo(screenPoints.get(i).x, screenPoints.get(i).y);
}
path.close();
canvas.drawPath(path, paint);
return bitmap;
}
你可以这样使用它:
Bitmap tileBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_forest); // create bitmap of fill icon
fillPoly(mGoogleMap.getProjection(), polygon, tileBitmap, 0.5f);
正确计算位图比例需要 mGoogleMap.getProjection()
。如果您需要在渲染 map 之前使用 mGoogleMap.getProjection()
,您应该使用 this andr的解决方案.
结果你得到了这样的结果:
如果您需要“图标之间有一些指定的空间”,那么空间应该位于图标本身内部:
(h - 水平空间,v - 垂直空间)。
地面叠加层始终随 map 缩放和旋转,如果您需要与缩放无关的图标,则应使用其他方法。此外,对于大型覆盖位图设备可能没有足够的内存。
3) 使用Tile Overlays
。实际上,它类似于 p.2,但为每个缩放级别生成叠加位图,并将其分割成小块(例如 256x256 像素)以避免高内存消耗。您可以将生成的图 block 存储在设备文件系统中并实现 TileProvider
就像 'this' Alex Vasilkov
的回答;
4) 使用custom
MapView
覆盖的基于 View dispatchDraw()
您可以在其中控制 View Canvas 上绘图的所有内容。这是最好的,但复杂的解决方案。
注意!这只是演示 - 不是完整的解决方案(例如,它没有测试负 LatLng 等)。
关于android - 用图标填充多边形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52268761/