ArcGIS for Android 自定义地图比例尺

      在我们进行地图相关开发时候,避免不了要绘制比例尺。在百度,高德的地图API里都提供了比例尺控件,但是ArcGIS for Android里并没有提供。不过没关系,我们可以自己绘制一个比例尺来。


      在绘制比例尺前,我们先了解几个概念:


  1. PPI,Pixels Per Inch的所写,表示的是每英寸所拥有的像素数目;
  2. PX,像素,表示图像中的一个最小单位;
  3. DPI,Dots Per Inch,每英寸点数,即图像密度;
  4. .9.PNG,Android开发里面的一种特殊的图片,这种格式的图片通过ADT自带的编辑工具生成,使用九宫格切分的方法,使图片支持在Android环境下的自适应展示。即这种类型图片在Android里无论怎样拉伸缩小都不失真。


      其中PPI和DPI在实际生活中的定义是不太一样的,而在Android里,他们的含义却是相似的。单独把DPI拿出来主要是Android里有个方法可以分别获取到屏幕X轴和Y轴的像素密度。


      .9.PNG格式的图片不失真,正好适合我们做来做比例尺图片。


       好了,我们好绘制一个比例尺,需要做些什么呢?


       首先,我们得知道当前地图比例,这个参数可以通过MapView.getScale来获取;


       其次,我们要根据当前地图比例绘制一个比例尺。绘制的方案有两种,一个是固定尺子长度,根据当前地图比例更换比例尺的比,比如1:2000,1:3000等;另一种是固定一些比例单位,比如1:50000后就是1:20000,然后比例尺的长度根据实际长度会做一定伸缩。这里我采用第二种,因为第一种根据实际比例往往比例尺的比值不是整数,并且在较大比例时候显示位数较长,四舍五入又会失去精度。


      最后,是监听地图放大缩小事件,并作出响应改变比例尺。


      方法就是这么简单,那就实际开动吧。


第一步,实例化地图,加载图层:

mMapView=(MapView)findViewById(R.id.mapview);
mMapScaleView=(MapScaleView)findViewById(R.id.scaleView);
String path= StorageUtil.getSDCardRootPath(getApplicationContext());
ArcGISLocalTiledLayer layer=new ArcGISLocalTiledLayer(path);
mMapView.addLayer(layer,0);
mMapScaleView.setMapView(mMapView);
ArcGISRuntime.setClientId(System.clint);//设置许可
mMapView.setMapBackground(0xFAFAFA, 0xffffff, 0.0f, 0.0f);//地图背景


第二步,自定义View,绘制比例尺:

      

      初始化自定义View:

public MapScaleView(Context context) {
    this(context, null);
    this.context=context;
    this.initVariables();
}

public MapScaleView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    this.context=context;
    this.initVariables();
}

public MapScaleView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    this.context=context;
    this.initVariables();
}


      初始化自定义View的参数:

private void initVariables(){
    scaleWidth=104;//
    scaleHeight=8;//比比例尺宽度例尺高度
    textColor= Color.BLACK;//比例尺字体颜色
    text="20公里";//比例尺文本
    textSize=18;//比例尺宽度
    scaleSpaceText=8;//比例尺文本与图形的间隔高度
    mPaint = new Paint();//画笔
}


      重写onMearsure()方法:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int widthSize = getWidthSize(widthMeasureSpec);
    int heightSize = getHeightSize(heightMeasureSpec);
    setMeasuredDimension(widthSize, heightSize);
}


      获取自定义View的宽和高:

**
 * 测量ScaleView的宽度
 * @param widthMeasureSpec
 * @return
 */
private int getWidthSize(int widthMeasureSpec){
    return MeasureSpec.getSize(widthMeasureSpec);
}

/**
 * 测量ScaleView的高度
 * @param heightMeasureSpec
 * @return
 */
private int getHeightSize(int heightMeasureSpec){
    int mode = MeasureSpec.getMode(heightMeasureSpec);
    int height = 0;
    switch (mode) {
        case MeasureSpec.AT_MOST:
            height = textSize + scaleSpaceText + scaleHeight;
            break;
        case MeasureSpec.EXACTLY:{
            height = MeasureSpec.getSize(heightMeasureSpec);
            break;
        }
        case MeasureSpec.UNSPECIFIED:{
            height = Math.max(textSize + scaleSpaceText + scaleHeight, MeasureSpec.getSize(heightMeasureSpec));
            break;
        }
    }

    return height;
}


      重写onDraw()方法,绘制比例尺:

**
 * 绘制上面的文字和下面的比例尺,因为比例尺是.9.png,我们需要利用drawNinepath方法绘制比例尺
 */
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    int width = scaleWidth ;
    mPaint.setColor(textColor);
    mPaint.setAntiAlias(true);
    mPaint.setTextSize(textSize);
    mPaint.setTypeface(Typeface.DEFAULT_BOLD);
    float textWidth = mPaint.measureText(text);
    canvas.drawText(text, (width - textWidth) / 2, textSize, mPaint);
    Rect scaleRect = new Rect(0, textSize + scaleSpaceText, width, textSize + scaleSpaceText + scaleHeight);
    drawNinepath(canvas, R.drawable.icon_scale, scaleRect);
}


      绘制.9.PNG图片:

private void drawNinepath(Canvas canvas, int resId, Rect rect){
    Bitmap bmp= BitmapFactory.decodeResource(getResources(), resId);
    NinePatch patch = new NinePatch(bmp, bmp.getNinePatchChunk(), null);
    patch.draw(canvas, rect);
}


      接下来就是根据获取的地图比例,绘制比例尺,更新比例尺的单位以及绘制它的长度,这里我按照2、5、1的进制选取了比例尺的单位:

public void refreshScaleView() {
        if(mapView == null){
            throw new NullPointerException("you can call setMapView(MapView mapView) at first");
        }
        double scale=this.mapView.getScale()/100;//结果单位米,表示图上1厘米代表*米
        double ppi=getPPIOfDevice();
        if(scale>0&&scale<=20){//换算20米
            String unit = "20米";
            int scaleWidth=(int)(20*264/2.54/scale);//264为ppi,264/2.54为1厘米的像素数
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>20&&scale<=50){//换算50米
            String unit = "50米";
            int scaleWidth=(int)(50*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>50&&scale<=100){//换算20米
            String unit = "100米";
            int scaleWidth=(int)(100*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>100&&scale<=200){//换算20米
            String unit = "200米";
            int scaleWidth=(int)(200*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>200&&scale<=500){//换算20米
            String unit = "500米";
            int scaleWidth=(int)(500*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>500&&scale<=1000){//换算20米
            String unit = "1公里";
            int scaleWidth=(int)(1000*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>1000&&scale<=2000){//换算20米
            String unit = "2公里";
            int scaleWidth=(int)(2000*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>2000&&scale<=5000){//换算20米
            String unit = "5公里";
            int scaleWidth=(int)(5000*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>5000&&scale<=10000){//换算20米
            String unit = "10公里";
            int scaleWidth=(int)(10000*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>10000&&scale<=20000){//换算20米
            String unit = "20公里";
            int scaleWidth=(int)(20000*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>20000&&scale<=25000){//换算20米
            String unit = "25公里";
            int scaleWidth=(int)(25000*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>25000&&scale<=50000){//换算20米
            String unit = "50公里";
            int scaleWidth=(int)(50000*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>50000&&scale<=100000){//换算20米
            String unit = "100公里";
            int scaleWidth=(int)(100000*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>100000&&scale<=200000){//换算20米
            String unit = "200公里";
            int scaleWidth=(int)(200000*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>200000&&scale<=250000){//换算20米
            String unit = "250公里";
            int scaleWidth=(int)(250000*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>250000&&scale<=500000){//换算20米
            String unit = "500公里";
            int scaleWidth=(int)(500000*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }else if(scale>500000&&scale<=scale){//换算20米
            String unit = "1000公里";
            int scaleWidth=(int)(1000000*264/2.54/scale);
            setText(unit);//更新文字
            setScaleWidth(scaleWidth);//更新比例尺长度
        }

        invalidate();
    }

      其中,获取屏幕PPI的方法如下:首先是用android.view包下的Display类里的getRealSize()方法获取屏幕分辨率;其次是用android.util包下有个DisplayMetrics类来获取密度相关的信息,该类里的xdpi和ydpi参数分别表示屏幕X轴和Y轴的点数密度,用X轴和Y轴的像素除以密度得到X轴和Y轴的真实长度,进而求得屏幕对角线的真实长度;最后用屏幕对角线的像素点数除以屏幕对角线的真实长度,得到屏幕的PPI:

private double getPPIOfDevice() {
    Point point = new Point();
    Activity activity=(Activity) context;
    activity.getWindowManager().getDefaultDisplay().getRealSize(point);//获取屏幕的真实分辨率
    DisplayMetrics dm = getResources().getDisplayMetrics();
    double x = Math.pow(point.x/ dm.xdpi, 2);//
    double y = Math.pow(point.y / dm.ydpi, 2);
    double screenInches = Math.sqrt(x + y);
    Double ppi=Math.sqrt(Math.pow(point.x, 2)+Math.pow(point.y, 2))/screenInches;
    return ppi;
}


第三步:写响应事件


      在ArcGIS的MapView有个监听地图大小变化的方法,叫setOnZoomListener(),在里面写响应事件即可:

mMapView.setOnZoomListener(new OnZoomListener() {
    @Override
    public void preAction(float v, float v1, double v2) {
        //todo
    }

    @Override
    public void postAction(float v, float v1, double v2) {
        mMapScaleView.refreshScaleView();
    }
});


      效果图如下:







  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 94
    评论
ArcGIS for Android 100天地图缓存功能是指在移动设备上通过使用ArcGIS for Android软件,可以对地理数据进行离线缓存,以实现在没有网络连接的情况下查看、分析和编辑地图数据的能力。这一功能对于在野外等无网络环境下进行地图工作的人员来说非常重要。 使用ArcGIS for Android软件,用户可以选择将需要的地理数据区域离线缓存到设备内存中,包括底图、图层、要素数据等。在网络连接正常时,可以通过在线地图服务加载并显示地图数据,而在无网络连接时,可以切换到离线缓存的地图数据,继续进行工作。 这种地图缓存功能有助于提高工作效率和数据安全性。通过离线缓存地图数据,用户不需要实时的网络连接就能够加载和查看地图数据,使得在无网络环境下依然能够完成地图分析和编辑工作。此外,由于数据保存在本地设备上,也减少了对网络连接和数据传输的依赖,提高了数据的安全性。 在实际应用中,用户可以根据需要选择缓存地图的详细程度和范围,以及缓存的数据类型。这样可以根据实际情况,减少对设备存储空间的占用,并提高数据加载和显示的速度。用户还可以随时更新地图缓存,以保持地图数据的最新性。 总而言之,ArcGIS for Android的100天地图缓存功能为用户提供了在无网络连接的情况下继续进行地图工作的能力,提高了工作效率和数据安全性。同时,用户可以根据实际需求进行灵活的地图缓存设置,以优化设备性能和减少存储空间的占用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值