通过ArcGISforAndroid在手机地图上动态绘制椭圆

项目要求在手机地图上动态的绘制椭圆,下图是实现的效果:
在地图上自由绘制椭圆
下面说说这个效果是如何实现的。
我们知道,Android可以通过画布Canvas绘制多种图形(详情点击: Android利用canvas画各种图形)。但是由于项目的特殊需求,所有的图形都必须在地图上绘制,所以Canvas类中的drawLine、drawCircle、drawOval等方法都不能直接使用。这就要求我们从底层开始写绘制椭圆的算法。
在地图上进行图形绘制相关工作,我们首先需要安装ArcGIS For Android。我们用的是ArcGIS For Android 10.1.1(SDK的安装及使用请参见:arcgis移动开发API)。
首先是绘制椭圆的算法。我们知道,椭圆的计算有两种方法,第一种就是极坐标公式,第二个是一般的椭圆公式。用手滑动绘制椭圆有一个点,那就是我们可以轻易获取起始点和终止点的位置。一旦知道这两个点,就可以得到椭圆的外切矩形,从而椭圆也就确定了。极坐标在绘制过程中需要考虑起始点和终止点的相对位置,表面上形式较为简单,但实际操作起来其实更麻烦。因此,我们最终决定通过椭圆的一般方程来绘制椭圆。
大家仔细观察上面的GIF图像,绘制过程中椭圆是有明显的棱角,手指抬起后棱角消失。其实,绘制过程中的椭圆是32条线段连接起来的,手指抬起时的椭圆是通过128条线段连接起来的(表面上看绘的是椭圆,实际上都是一条一条短直线)。
获取椭圆128个椭圆数据点点集的代码如下(32个椭圆点的代码与此类似):

/**
     * 下面两个函数通过输入椭圆的中心点、长半轴和短半轴获取该椭圆的点集,分为两种,第一种只有32个点(粗略点集,显示在绘制过程中),第二种有128个点(详细点集,显示在最后绘制的椭圆中)。
     *       默认输入的坐标点和长短半轴已经由屏幕坐标转换为地理坐标。
     *@param centerPoint   椭圆的中心点
     *@param  longAxis     椭圆的长半轴
     *@param  shortAxis    椭圆的短半轴(此处长短半轴只表示横轴纵轴,而不表示实际长短对比)
     *@author Liu Maolin
     *@since  2016-02-26 20:47
**/
    private ArrayList<Point> getDetailedOvalPoints(Point centerPoint, double longAxis, double shortAxis){
        ArrayList<Point> pointsSet = new ArrayList<Point>();
        double[][] xy = new double[32][2]; 
        //求出第一象限所有的x值和y值,通过图像的对称性减少运算;
        for(int i=1;i<32;i++){
            xy[i][0] = longAxis*(1-i/32.0);                                          //将8改为8.0对于最后结果至关重要!!!!!!!!!!!!!!!!!!!!!!!!!!!
            xy[i][1] = Math.sqrt(1-(xy[i][0]*xy[i][0]/longAxis/longAxis))*shortAxis;
            Log.d("maolin:","****************************************values of xy["+ i +"] are: " + xy[i][0]+" , "+ xy[i][1]);
        }
        pointsSet.add(new Point(centerPoint.getX()+longAxis, centerPoint.getY()));
        //第一象限
        for(int j=1;j<32;j++){
            pointsSet.add(new Point(centerPoint.getX()+xy[j][0], centerPoint.getY()+xy[j][1]));

        }
        //y轴正半轴与椭圆的交点;
        pointsSet.add(new Point(centerPoint.getX(), centerPoint.getY()+shortAxis));
        //第二象限
        for(int j=1;j<32;j++){
            pointsSet.add(new Point(centerPoint.getX()-xy[32-j][0], centerPoint.getY()+xy[32-j][1]));
        }
        //X轴负半轴与椭圆的交点;
        pointsSet.add(new Point(centerPoint.getX()-longAxis, centerPoint.getY()));
        //第三象限
        for(int j=1;j<32;j++){
            pointsSet.add(new Point(centerPoint.getX()-xy[j][0], centerPoint.getY()-xy[j][1]));
        }
        //y轴负半轴与椭圆的交点;
        pointsSet.add(new Point(centerPoint.getX(), centerPoint.getY()-shortAxis));
        //第四象限
        for(int j=1;j<32;j++){
            pointsSet.add(new Point(centerPoint.getX()+xy[32-j][0], centerPoint.getY()-xy[32-j][1]));
        }


        return pointsSet;
    }

从上面的代码可以看出,要想获得椭圆的点集,我们必须有三个参数,分别是椭圆中心点,椭圆长半轴和椭圆短半轴。其实,我们只需要绘制椭圆的起始点和终止点就可以,上面三个参数可以直接通过这两个点计算得到。假设起始点是startPoint,终止点是currentPoint,那么点集可通过下面代码获取:

centerPoint = new Point((startPoint.getX() + currentPoint.getX())/2,(startPoint.getY() + currentPoint.getY())/2);   //得到中心点
pointsSets = getRoughOvalPoints(centerPoint,Math.abs((startPoint.getX() - currentPoint.getX()))/2,Math.abs((startPoint.getY() - currentPoint.getY()))/2);   //得到椭圆的点集

所以,下面问题的关键就是随着手指的滑动实时获取椭圆起始点和终止点了。椭圆的起始点在手指落入手机屏幕那一瞬间就确定了,问题的关键是随着手指的移动不断地获取椭圆的终止点。我们需要用到ArcGIS For Android里面的MapOnTouchListener(详情见MapOnTouchListener),这里需要用到里面的两个方法,一个是onDragPointerMove(),通过它可以实时获取手指在地图上的位置,另一个是onDragPointerUp(),即绘制完成手指在地图上的位置。具体代码如下:

private class MyMapOnTouchListener extends MapOnTouchListener {
    Graphic drawingMultiPathGraphic;
    ArrayList<Point> pointsSets = new ArrayList<Point>();

    public MyMapOnTouchListener(Context context, MapView view) {
        super(context, view);
        // TODO Auto-generated constructor stub
    }

    @Override
    public boolean onDragPointerMove(MotionEvent from, MotionEvent to) {
        Point centerPoint;                      
        if (startPoint == null) {
            startPoint = mMapView.toMapPoint(from.getX(), from.getY());  //起始点
            multipath = new Polygon();                  
            drawingMultiPathGraphic = new Graphic(multipath, new SimpleLineSymbol(Color.BLUE, LINE_SIZE));
            multipath.startPath(startPoint);        
            currentPoint = mMapView.toMapPoint(to.getX(), to.getY());  //手指滑动的终止点           
            centerPoint = new Point((startPoint.getX() + currentPoint.getX())/2,(startPoint.getY() + currentPoint.getY())/2);   //得到中心点
            if(!pointsSets.isEmpty()){
                pointsSets.clear();
            }               
            pointsSets = getRoughOvalPoints(centerPoint,Math.abs((startPoint.getX() - currentPoint.getX()))/2,Math.abs((startPoint.getY() - currentPoint.getY()))/2);               
            if(!multipath.isEmpty()){
                multipath.setEmpty();                           
                }                   
            multipath.startPath(pointsSets.get(0));
            for (int i = 1; i < pointsSets.size(); i++) {
                multipath.lineTo(pointsSets.get(i));
            }

            //更新ID
            if (tempOvalID != Integer.MIN_VALUE) {
                drawLayer.updateGraphic(tempOvalID, drawingMultiPathGraphic);
            } else {
                tempOvalID = drawLayer.addGraphic(drawingMultiPathGraphic);
            }
                break;                                      
                return true;
            }
            return super.onDragPointerMove(from, to);
        }

        @Override
        public boolean onDragPointerUp(MotionEvent from, MotionEvent to) {

        if(drawType == DrawType.DRAW_OVAL){
            Point centerPoint = new Point((startPoint.getX() + currentPoint.getX())/2,(startPoint.getY() + currentPoint.getY())/2);
            pointsSets.clear();
            pointsSets = getDetailedOvalPoints(centerPoint,Math.abs((startPoint.getX() - currentPoint.getX()))/2,Math.abs((startPoint.getY() - currentPoint.getY()))/2);
            multipath.setEmpty();
            multipath.startPath(pointsSets.get(0));
            for (int i = 1; i < pointsSets.size(); i++) {
                multipath.lineTo(pointsSets.get(i).getX(), pointsSets.get(i).getY());
            }
            if (tempOvalID != Integer.MIN_VALUE) {
                drawLayer.updateGraphic(tempOvalID, drawingMultiPathGraphic);
                Log.i("Lucas", String.valueOf(tempOvalID));
            } else {
                tempOvalID = drawLayer.addGraphic(drawingMultiPathGraphic);
                Log.i("Lucas", String.valueOf(tempOvalID));
            }
        }
        return super.onDragPointerUp(from, to);
    }
}

应该注意到,在onDragPointerMove()方法中获取的椭圆点集是32个点的点集,在onDragPointerUp()方法中获取的椭圆点集是128个点的点集。这是因为onDragPointerMove()可以看做一个循环函数,手指在屏幕上滑动过程中,位置改变一次,该函数就执行一次。随着手指的滑动,一般至少会绘制几十次椭圆,如果这个时候就绘制128个点的椭圆,对手机的压力比较大,所以我们选择在手指滑动的过程中绘制32个点的椭圆,在手指抬起后绘制128个点的椭圆。
之后就是如何使用上面这个类了。在MapView对象mMapView中设置监听即可:

mMapView.setOnTouchListener(new MyMapOnTouchListener(ArcGISMapBaseActivity.this, mMapView));

这样就可以完成上面椭圆的动态绘制了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值