Mapbox地图画布上的自定义绘图 [英] Custom Drawing on Mapbox Map Canvas

查看:1621
本文介绍了Mapbox地图画布上的自定义绘图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



这是我到目前为止对mapbox唯一的代码:

  @Override 
public void onDraw(Canvas canvas){
super.onDraw(canvas);

Paint stroke = new Paint();
stroke.setColor(Color.BLACK);
stroke.setStyle(Paint.Style.STROKE);
stroke.setStrokeWidth(5);
stroke.setAntiAlias(true);

canvas.drawLine(0f,0f,1440f,2464f,stroke);
}

解决方案

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



1)如你所说:继承 MapView 类并重写 onDraw()事件。 MapView extends FrameLayout 这是 ViewGroup ,所以你应该覆盖 dispatchDraw()而不是 onDraw()



这方法需要自定义视图,扩展 MapView 并实现:




  • 绘图超过 MapView ;


  • 自定义线条样式(钻石代替简单线条);


  • 绑定路径 Lat / Lon 坐标 MapView




对于drawin g超过 MapView 你应该覆盖 dispatchDraw(),例如:

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

自定义线条样式您可以使用



你应该考虑一些特殊情况,例如所有点路径在地图的当前视图之外,没有任何线条,甚至线应该是地图的交叉视图,应该是可见的。



2)(更好方式)使用您的附加行创建和发布地图并自定义样式对于他们(特别是看看带图像的线条图案部分)。您可以使用 Mapbox Studio 。在这种方法中,所有特殊情况和性能问题都在Mabpox方面得到解决。


I would like to be able to manually draw complex shapes on a mapbox map using the android sdk. I have inherited the map view class and overridden the ondraw event but unfortunately whatever I draw gets over drawn by the map itself.

As an example I need to be able to draw polygons with diamond shaped borders among other complex shapes. This i can do no problem in GoogleMaps using a custom tile provider and overriding ondraw.

Here is the only code I have so far for mapbox:

    @Override
    public void onDraw(Canvas canvas) {        
        super.onDraw(canvas);

        Paint stroke = new Paint();
        stroke.setColor(Color.BLACK);
        stroke.setStyle(Paint.Style.STROKE);
        stroke.setStrokeWidth(5);
        stroke.setAntiAlias(true); 

        canvas.drawLine(0f,0f,1440f,2464f,stroke);
    }

解决方案

You can do what You want by 2 ways:

1) as You propose: "inherit the MapView class and overridden the onDraw() event". But MapView extends FrameLayout which is ViewGroup, so You should override dispatchDraw() instead of onDraw().

This approach requires custom view, which extends MapViewand implements:

  • drawing over the MapView;

  • customizing line styles ("diamonds instead of a simple line");

  • binding path to Lat/Lon coordinates of MapView.

For drawing over the MapView You should override dispatchDraw(), for example like this:

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

For customizing line styles You can use setPathEffect() method of Paint class. For this You should create path for "diamond stamp" (in pixels), which will repeated every "advance" (in pixels too):

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

(in this case there are 2 Path - first one (clockwise) for outer border and second (counter-clockwise) for inner border for "diamond" transparent "hole").

For binding path on screen to Lat/Lon coordinates of MapView You should have MapboxMap object of MapView - for that getMapAsync() and onMapReady() should be overridden:

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

Than You can use it in "lat/lon-to-screen" convertating:

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

Full source code:

Custom DrawMapView.java

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

ActivityMain.java

public class MainActivity extends AppCompatActivity {

    private DrawMapView mapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        MapboxAccountManager.start(this, getString(R.string.access_token));
        setContentView(R.layout.activity_main);

        mapView = (DrawMapView) findViewById(R.id.mapView);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(MapboxMap mapboxMap) {

                mapView.setBorderPoints(Arrays.asList(new LatLng(-36.930129, 174.958843),
                        new LatLng(-36.877860, 174.978108),
                        new LatLng(-36.846373, 174.901841),
                        new LatLng(-36.829215, 174.814659),
                        new LatLng(-36.791326, 174.779337),
                        new LatLng(-36.767680, 174.823242)));
            }
        });
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:mapbox="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="ua.com.omelchenko.mapboxlines.MainActivity">

    <ua.com.omelchenko.mapboxlines.DrawMapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        mapbox:center_latitude="-36.841362"
        mapbox:center_longitude="174.851110"
        mapbox:style_url="@string/style_mapbox_streets"
        mapbox:zoom="10"/>
</RelativeLayout>

Finally, as a result, you should get something like this:

And You should take into account some "special cases", for example if all points of path is outside current view of map, there is no lines on it, even line should cross view of map and should be visible.

2) (better way) create and publish map with your additional lines and custom style for them (especially take a look at "Line patterns with Images" sections). You can use Mapbox Studio for this. And in this approach all "special cases" and performance issues is solved on Mabpox side.

这篇关于Mapbox地图画布上的自定义绘图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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