Mapbox地图画布上的自定义绘图

时间:2016-09-19 00:19:37

标签: java android canvas mapbox

我希望能够使用android sdk在地图集地图上手动绘制复杂的形状。我继承了地图视图类并重写了ondraw事件,但不幸的是,我绘制的任何东西都被地图本身绘制了。

作为一个例子,我需要能够在其他复杂形状中绘制带有菱形边框的多边形。我可以使用自定义磁贴提供程序并覆盖ondraw在GoogleMaps中没有问题。

以下是我目前为mapbox提供的唯一代码:

func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {

        if segue.identifier == "showImagePicker" {

            print("This is the Image Picker")

        }

        if segue.identifier == "showNamePicker"  {

            print("This is the Name Picker")

        } 
}

enter image description here

2 个答案:

答案 0 :(得分:11)

你可以通过两种方式做你想做的事:

1)正如您所建议的那样:"继承MapView类并覆盖onDraw()事件"。MapView extends {{ 1}}这是FrameLayout,因此您应该覆盖ViewGroup而不是dispatchDraw()

此方法需要自定义视图,该视图扩展onDraw()并实现:

  • 绘制MapView;

  • 自定义线条样式("钻石而不是简单的线条");

  • MapView的{​​{1}}坐标的绑定路径。

用于绘制Lat/Lon您应该覆盖MapView,例如:

MapView

自定义线条样式您可以使用dispatchDraw()类的setPathEffect()方法。为此,您应该为"钻石印章"创建路径。 (以像素为单位),这将重复每一次"前进" (也以像素为单位):

@Override
public void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    canvas.save();
    drawDiamondsPath(canvas);
    canvas.restore();
}

(在这种情况下有2 Paint - 第一个(顺时针)用于外边框,第二个(逆时针)用于内边框用于"钻石"透明"孔&# 34。)

对于屏幕上的 mPathDiamondStamp = new Path(); mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2, 0); mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2); mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2, 0); mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2); mPathDiamondStamp.close(); mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2 + DIAMOND_BORDER_WIDTH, 0); mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2 + DIAMOND_BORDER_WIDTH / 2); mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2 - DIAMOND_BORDER_WIDTH, 0); mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2 - DIAMOND_BORDER_WIDTH / 2); mPathDiamondStamp.close(); mPathDiamondStamp.setFillType(Path.FillType.EVEN_ODD); mDiamondPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mDiamondPaint.setColor(Color.BLUE); mDiamondPaint.setStrokeWidth(2); mDiamondPaint.setStyle(Paint.Style.FILL_AND_STROKE); mDiamondPaint.setStyle(Paint.Style.STROKE); mDiamondPaint.setPathEffect(new PathDashPathEffect(mPathDiamondStamp, DIAMOND_ADVANCE, DIAMOND_PHASE, PathDashPathEffect.Style.ROTATE)); 坐标Path的绑定路径您应该Lat/Lon MapView对象 - MapboxMapMapView应该被覆盖:

getMapAsync()

比你可以在" lat / lon-to-screen" convertating:

onMapReady()

完整源代码:

自定义DrawMapView.java

@Override
public void getMapAsync(OnMapReadyCallback callback) {
    mMapReadyCallback = callback;
    super.getMapAsync(this);
}

@Override
public void onMapReady(MapboxMap mapboxMap) {
    mMapboxMap = mapboxMap;
    if (mMapReadyCallback != null) {
        mMapReadyCallback.onMapReady(mapboxMap);
    }
}

ActivityMain.java

        mBorderPath = new Path();
        LatLng firstBorderPoint = mBorderPoints.get(0);
        PointF firstScreenPoint = mMapboxMap.getProjection().toScreenLocation(firstBorderPoint);
        mBorderPath.moveTo(firstScreenPoint.x, firstScreenPoint.y);

        for (int ixPoint = 1; ixPoint < mBorderPoints.size(); ixPoint++) {
            PointF currentScreenPoint = mMapboxMap.getProjection().toScreenLocation(mBorderPoints.get(ixPoint));
            mBorderPath.lineTo(currentScreenPoint.x, currentScreenPoint.y);
        }

activity_main.xml中

public class DrawMapView extends MapView implements OnMapReadyCallback{

    private float DIAMOND_WIDTH = 42;
    private float DIAMOND_HEIGHT = 18;
    private float DIAMOND_ADVANCE = 1.5f * DIAMOND_WIDTH;       // spacing between each stamp of shape
    private float DIAMOND_PHASE = DIAMOND_WIDTH / 2;            // amount to offset before the first shape is stamped
    private float DIAMOND_BORDER_WIDTH = 6;                     // width of diamond border

    private Path mBorderPath;
    private Path mPathDiamondStamp;
    private Paint mDiamondPaint;
    private OnMapReadyCallback mMapReadyCallback;
    private MapboxMap mMapboxMap = null;
    private List<LatLng> mBorderPoints;

    public DrawMapView(@NonNull Context context) {
        super(context);
        init();
    }

    public DrawMapView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public DrawMapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public DrawMapView(@NonNull Context context, @Nullable MapboxMapOptions options) {
        super(context, options);
        init();
    }

    public void setBorderPoints(List<LatLng> borderPoints) {
        mBorderPoints = borderPoints;
    }

    @Override
    public void getMapAsync(OnMapReadyCallback callback) {
        mMapReadyCallback = callback;
        super.getMapAsync(this);
    }

    @Override
    public void onMapReady(MapboxMap mapboxMap) {
        mMapboxMap = mapboxMap;
        if (mMapReadyCallback != null) {
            mMapReadyCallback.onMapReady(mapboxMap);
        }
    }

    @Override
    public void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        canvas.save();
        drawDiamondsPath(canvas);
        canvas.restore();
    }

    private void drawDiamondsPath(Canvas canvas) {
        if (mBorderPoints == null || mBorderPoints.size() == 0) {
            return;
        }

        mBorderPath = new Path();

        LatLng firstBorderPoint = mBorderPoints.get(0);
        PointF firstScreenPoint = mMapboxMap.getProjection().toScreenLocation(firstBorderPoint);
        mBorderPath.moveTo(firstScreenPoint.x, firstScreenPoint.y);

        for (int ixPoint = 1; ixPoint < mBorderPoints.size(); ixPoint++) {
            PointF currentScreenPoint = mMapboxMap.getProjection().toScreenLocation(mBorderPoints.get(ixPoint));
            mBorderPath.lineTo(currentScreenPoint.x, currentScreenPoint.y);
        }

        mPathDiamondStamp = new Path();
        mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2, 0);
        mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2);
        mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2, 0);
        mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2);
        mPathDiamondStamp.close();

        mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2 + DIAMOND_BORDER_WIDTH, 0);
        mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2 + DIAMOND_BORDER_WIDTH / 2);
        mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2 - DIAMOND_BORDER_WIDTH, 0);
        mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2 - DIAMOND_BORDER_WIDTH / 2);
        mPathDiamondStamp.close();

        mPathDiamondStamp.setFillType(Path.FillType.EVEN_ODD);

        mDiamondPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mDiamondPaint.setColor(Color.BLUE);
        mDiamondPaint.setStrokeWidth(2);
        mDiamondPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mDiamondPaint.setStyle(Paint.Style.STROKE);
        mDiamondPaint.setPathEffect(new PathDashPathEffect(mPathDiamondStamp, DIAMOND_ADVANCE, DIAMOND_PHASE, PathDashPathEffect.Style.ROTATE));

        canvas.drawPath(mBorderPath, mDiamondPaint);
    }

    private void init() {
        mBorderPath = new Path();
        mPathDiamondStamp = new Path();
    }
}

最后,结果,你应该得到这样的东西:

enter image description here

你应该考虑一些&#34;特殊情况&#34;,例如,如果所有路径都在地图的当前视图之外,那么就没有线,甚至线应该跨越地图的视图,应该可见。

2)(更好的方法)使用您的附加行创建和发布地图,并为他们custom style (特别是看看&#34;带有图片的线条图案&#34;部分)。您可以使用Mapbox Studio。在这种方法中,所有&#34;特殊情况&#34; Mabpox方面解决了性能问题。

答案 1 :(得分:6)

如果我理解正确,你试图在地图上添加钻石形状(用户不是在绘制形状)?如果是这种情况,您可以选择以下几种方法:

  1. 使用多边形,只需添加点列表,它就会绘制形状(在本例中为菱形)。这将是最简单的,但我认为你已经尝试过它并不适合你。

    List<LatLng> polygon = new ArrayList<>();
    polygon.add(<LatLng Point 1>);
    polygon.add(<LatLng Point 2>);
    
    ...
    
    mapboxMap.addPolygon(new PolygonOptions()
      .addAll(polygon)
      .fillColor(Color.parseColor("#3bb2d0")));
    
  2. 使用4.2.0中引入的新Style API(仍在测试版中)添加填充图层。这样做首先要求您使用点创建GeoJSON对象,然后将其添加到地图中。我必须做的最接近的例子是this example,可以在演示应用程序中找到。

  3. 使用onDraw,只需将画布转换为GeoJSON对象,然后添加为第2步中说明的图层。如果您在运行时让用户绘制形状,则仅推荐使用这种情况下坐标是不确定的。

  4. 如果您正在寻找不同的内容,我会编辑此答案。