如何在Android中的Google地图上绘制网格? [英] How to draw a grid over google maps in android?

查看:152
本文介绍了如何在Android中的Google地图上绘制网格?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个类似于What3Words应用程序的网格.

相机缩放到某个级别之后,网格就会显示出来,并随着用户缩放而缩放.

I want to create a grid like What3Words app did.

Once the camera zooms past a certain level the grid shows up and scales in size as the user zooms.

我尝试了TileOverlay并成功创建了一个网格.问题在于,网格在每次缩放时都会重绘.我希望使用缩放级别来缩放网格,而不是重新绘制网格.

I've tried TileOverlay and successfully created a grid. The problem was that the grid redraws itself with every zoom. I want the grid rather than redrawn, to be scaled with the zoom level.

然后我继续使用GroundOverlay并使用了已经绘制的网格,发现了两个问题:图像质量比原始图像差,并且像TileOverlay一样,它不随缩放缩放.

I've then moved on to GroundOverlay and used an already drawn grid and I found two issues: The image quality is worse than the original and, like the TileOverlay, it doesn't scale with the zooming.

然后我尝试使用多边形绘制正方形并使用(Map.getProjection().getVisibleRegion())提供的经度和纬度,并且由于地球是一个球体,因此网格的大小在不同区域上不一致.

Then I tried to use polygons to draw the squares and use the longitude and latitude provided by (Map.getProjection().getVisibleRegion()) and because the earth is a sphere the grid's size is inconsistent on different areas.

现在我正在使用画布手动绘制它.

And now I'm using a canvas to draw it manually.

你们中的任何一个人不知道如何实现我想做的事情吗?

Does any of you have any idea how to achieve what I'm trying to do?

谢谢.

推荐答案

好,此答案演示了图形更新以绘制和移动网格,并尝试使用w3w API对齐网格.

Ok, this answer demonstrates the graphic update to draw and move the grid and also an attempt to align the grid using the w3w API.

因此使用w3w的一个问题似乎是如何计算 网格单元.由于该算法显然是私有的,因此对于此实现 "grid" rest api调用用于当前屏幕中心点(处于空闲状态),而json响应已解析为候选参考点.

So one issue with using the w3w seems to be how to compute the location of a grid cell. Since the algorithm evidently is private, for this implementation the 'grid' rest api call is used for the current screen center point (on idle) and the json response parsed for a candidate reference point.

在此示例中,始终为参考"网格单元绘制一个多边形 是从w3w网格调用获得的.

In this example a polygon is always drawn for the "reference" grid cell obtained from the w3w grid call.

网格视图实现使用canvas.translate调用正确对齐 并使用从参考点计算出的偏移量绘制网格.

The grid view implementation uses the canvas.translate call to properly align and draw the grid using an offset computed from the reference point.

由于使用了SphericalUtil用法将距离映射到屏幕像素,因此该方法在任何纬度下都有效.

This works at any latitude due to the use of the SphericalUtil usage for mapping distance to screen pixels.

底部录制(低质量).

主要活动

这里,w3w网格静止调用是在摄像机闲置时进行的,并且可以进行足够远的缩放(无需继续以近距缩放进行重新对齐)和结果(附近的网格单元的角点将用作 参考点)输入到网格视图.绘制一个填充的多边形来表示 参考网格单元.

Here the w3w grid rest call is made on camera idle and zoom far enough out (there's no need to keep realigning in close zoom) and the result (a corner point of a nearby grid cell to be used as a reference point) is fed to the grid view. A filled polygon is drawn to denote the reference grid cell.

在摄像机移动时,使用相同的参考点,但使用当前屏幕位置来保持适当的偏移量.

On camera movement, the same reference point is used but the current screen position is used to maintain the proper offsets.

public void what3words() {

    // some initial default position
    LatLng pt = new LatLng(38.547279, -121.461019);

    mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);

    // move map to a coordinate
    CameraUpdate cu = CameraUpdateFactory.newLatLng(pt);
    mMap.moveCamera(cu);
    cu = CameraUpdateFactory.zoomTo(14);
    mMap.moveCamera(cu);

    mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
        @Override
        public void onMapClick(LatLng latLng) {
            mMap.addCircle(new CircleOptions().radius(4).strokeColor(Color.BLUE).center(latLng));
        }
    });

    // while the camera is moving just move the grid (realign on idle)
    mMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
        @Override
        public void onCameraMove() {
            ((GridView) findViewById(R.id.grid_any)).setAlignment(
                    null, mMap.getProjection(), mMap.getProjection().getVisibleRegion());
        }
    });

    // on idle fetch the grid using the screen center point
    mMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
        @Override
        public void onCameraIdle() {
            Log.d(TAG,"idle");


            final LatLng centerOfGridCell = mMap.getCameraPosition().target;

            if (!gridSet || mMap.getCameraPosition().zoom < 10) {
                getGrid(centerOfGridCell, new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        Log.d(TAG, "reqpt: " + centerOfGridCell + " volley response: " + response);
                        try {
                            JSONObject jsonObject = new JSONObject(response);
                            JSONArray jsonArray = jsonObject.getJSONArray("lines");
                            JSONObject firstList = jsonArray.getJSONObject(1);
                            JSONObject firstPt = firstList.getJSONObject("start");
                            String lat = firstPt.getString("lat");
                            String lng = firstPt.getString("lng");
                            Log.d(TAG, "lat: " + lat + " lng: " + lng);

                            LatLng alignmentPt = new LatLng(Double.parseDouble(lat), Double.parseDouble(lng));
                            Projection p = mMap.getProjection();
                            VisibleRegion vr = p.getVisibleRegion();


                            ((GridView) findViewById(R.id.grid_any)).setAlignment(alignmentPt, p, vr);

                            if (polygon != null) {
                                polygon.remove();
                            }

                            // take alignment point and draw 3 meter square polygon
                            LatLng pt1 = SphericalUtil.computeOffset(alignmentPt, 3, 90);
                            LatLng pt2 = SphericalUtil.computeOffset(pt1, 3, 180);
                            LatLng pt3 = SphericalUtil.computeOffset(pt2, 3, 270);

                            polygon = mMap.addPolygon(new PolygonOptions().add(alignmentPt,
                                    pt1, pt2, pt3, alignmentPt)
                                    .strokeColor(Color.BLUE).strokeWidth(4).fillColor(Color.BLUE));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                });

                gridSet = true;
            }


        }
    });
}


// Issue request to w3w - REMEMBER TO REPLACE **YOURKEY** ...
private void getGrid(LatLng pt, Response.Listener<String> listener) {

    // something 9 meters to east
    LatLng otherPt = SphericalUtil.computeOffset(pt, 6.0, 225);
    String bboxStr = Double.toString(pt.latitude)+","+
            Double.toString(pt.longitude)+","+
            Double.toString(otherPt.latitude)+","+
            Double.toString(otherPt.longitude);
    RequestQueue rq = Volley.newRequestQueue(this);
    String url = "https://api.what3words.com/v2/grid?bbox="+bboxStr+"&format=json&key=YOURKEY";

    Log.d(TAG,"url="+url);
    StringRequest req = new StringRequest(Request.Method.GET, url, listener, new Response.ErrorListener() {

        @Override
        public void onErrorResponse(VolleyError error) {
            Log.e(TAG, "volley error: "+error);
        }
    });

    rq.add(req);
}

网格视图

网格视图扩展了View,并且在地图布局中作为地图片段的同级.

The grid view extends View and is in the map layout as a sibling to the map fragment.

有趣的部分是:

setAlignment -这里的3米屏幕像素范围是使用 SphericalUtil类.屏幕像素尺寸代表3米长 (在提供的参考位置)(然后用于通过计算x/y偏移量对齐网格)(然后在onDraw中使用).请注意,这会使用"SphericalUtil.computeOffset"实用程序自动缩放网格,以测量东3米的一个点,从而计算出3米的屏幕像素等效值.

setAlignment - here the screen pixel extent of 3 meters is computed using the SphericalUtil class. This screen pixel dimension representing a 3 meter extent (at the provided reference location) is then used to align the grid by computing x/y offsets (which are then used in the onDraw). Note this auto-scales the grid using the 'SphericalUtil.computeOffset' utility to measure a point 3 meters east and as a result compute the screen pixel equivalent of 3 meters.

onDraw -这里的canvas转换方法用于从计算的偏移量开始(在setAlignment中)重复网格形状.

onDraw - here the translate method of canvas is used to repeat the grid shape starting at the computed offset (in setAlignment).

public class GridView extends View {

    private static final String TAG = GridView.class.getSimpleName();

    BitmapDrawable bm;
    Bitmap bitmap;

    GradientDrawable gd;
    int offsetX = 0;
    int offsetY = 0;
    private int width = 16;


    public GridView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    public void setWidth(int w) {
        width = w;
        render();
        invalidate();
    }


    private void render() {
        setShape();
        if (gd != null) {
            bitmap = drawableToBitmap(gd);
            bm = new BitmapDrawable(getResources(), bitmap);
            bm.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
            bm.setBounds(0, 0, width, width);
        }
    }

    Point startPt;
    LatLng savedAlignmentPt;


    public void setAlignment(LatLng alignmentPt, Projection p, VisibleRegion vr) {

        if (alignmentPt == null) {
            alignmentPt = savedAlignmentPt;
        }

        if (alignmentPt == null) {
            return;
        }
        // the alignment point is the a corner of a grid square "near" the center of the screen
        savedAlignmentPt = alignmentPt;

        // compute how many screen pixels are in 3 meters using alignment point
        startPt =  p.toScreenLocation(alignmentPt);
        LatLng upperRight = SphericalUtil.computeOffset(alignmentPt, 3, 90);
        Point upperRtPt = p.toScreenLocation(upperRight);

        int pixelsOf3meters = upperRtPt.x - startPt.x;

        // don't draw grid if too small
        if (pixelsOf3meters < 16) {
            return;
        }

        setWidth(pixelsOf3meters);

        // startPt is screen location of alignment point
        offsetX = (pixelsOf3meters - (startPt.x % pixelsOf3meters));
        offsetY = (pixelsOf3meters - (startPt.y % pixelsOf3meters));

        invalidate();

    }

    private void setShape() {
        int left, right, top, bottom;
        gd = new GradientDrawable();
        gd.setShape(GradientDrawable.RECTANGLE);
        gd.setSize(width, width);
        gd.setStroke(2, Color.argb(0xff, 0xcc, 0x22, 0x22));

    }

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

        Rect rect = canvas.getClipBounds();


        final int cWidth = canvas.getWidth();
        final int cHeight = canvas.getHeight();

        if (bm == null) {
            return;
        }

        final Rect bmRect = bm.getBounds();
        if (bmRect.width() <= 8 || bmRect.height() <= 8) {
            return;
        }


        final int iterX = iterations(cWidth, -offsetX, bmRect.width());
        final int iterY = iterations(cHeight, -offsetY, bmRect.height());

        canvas.translate(-offsetX, -offsetY);

        for (int x = 0; x < iterX; x++) {
            for (int y = 0; y < iterY; y++) {
                bm.draw(canvas);
                canvas.translate(.0F, bmRect.height());
            }
            canvas.translate(bmRect.width(), -bmRect.height() * iterY);
        }
    }

    private static int iterations(int total, int start, int side) {
        final int diff = total - start;
        final int base = diff / side;
        return base + (diff % side > 0 ? 1 : 0);
    }

    public static Bitmap drawableToBitmap (Drawable drawable) {
        Bitmap bitmap = null;

        if (drawable instanceof BitmapDrawable) {
            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            if(bitmapDrawable.getBitmap() != null) {
                return bitmapDrawable.getBitmap();
            }
        }

        if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
            bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
        } else {
            bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        }

        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
        return bitmap;
    }
}

注意:

  • The translate stuff was referenced from: https://github.com/noties/ScrollingBackgroundView/blob/master/library/src/main/java/ru/noties/sbv/ScrollingBackgroundView.java.
  • The w3w grid api result is not well documented so I just picked a point in the returned list as a candidate cell corner point.
  • The 'getGrid' method simply uses volley to issue the w3w request and most of the main activity code is in the response listener: https://developer.android.com/training/volley/

我正在与w3w网格API进行自己的斗争.当我为返回列表中的每个点计算起点/终点之间的距离时,我得到4.24264米,因此很明显我什么也没得到.这是一种显示结果和屏幕截图的简单方法(白色=请求中使用的当前中心,其他任何颜色=列表中点对的起点-终点;终点具有黑色轮廓).在这里也可以清楚地看到哪个点用于对齐网格.

I'm having my own battle with the w3w grid API. When I compute the distance between start/end points for each point in the list returned, I get 4.24264 meters so clearly I'm not getting something. Here's a simple method to show results and the screen shot (white=current center used in request, any other color=start-end of a point pair in list; end points have black outline). Here also it becomes clear which point is used to align the grid.

有趣的是,一条线"的起点确实距离下一条线的起点3米(比较红色起点与蓝色起点):

Interestingly though, the start of one "line" does appear to be 3 meters from the start of the next line (compare red-start to blue-start):

代码:

 // plot each point as a circle
 for (int i = 0; i < jsonArray.length(); i++) {
     JSONObject startPt = jsonArray.getJSONObject(i).getJSONObject("start");
     JSONObject endPt = jsonArray.getJSONObject(i).getJSONObject("end");
     LatLng start = new LatLng(Double.parseDouble(startPt.getString("lat")), Double.parseDouble(startPt.getString("lng")));
     LatLng end = new LatLng(Double.parseDouble(endPt.getString("lat")), Double.parseDouble(endPt.getString("lng")));
     int c = colors[(i % colors.length)];
     mMap.addCircle(new CircleOptions().center(start).strokeColor(c).fillColor(c).radius(1));
     mMap.addCircle(new CircleOptions().center(end).strokeColor(Color.BLACK).fillColor(c).radius(1).strokeWidth(2));

     Log.d(TAG, "d  = "+SphericalUtil.computeDistanceBetween(start,end));
 }
 mMap.addCircle(new CircleOptions().center(centerOfGridCell).strokeColor(Color.WHITE).radius(1).strokeWidth(4));

这篇关于如何在Android中的Google地图上绘制网格?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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