Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/186.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android Google Maps API中的数千个多边形是主线程的负担_Android_Api_Maps_Polygons - Fatal编程技术网

Android Google Maps API中的数千个多边形是主线程的负担

Android Google Maps API中的数千个多边形是主线程的负担,android,api,maps,polygons,Android,Api,Maps,Polygons,我正在开发一个应用程序,从一个文本文件中从给定的GPS位置绘制运动路径。 到目前为止,我已经成功地加载了数据并绘制了路径。 我的解决方案是,路径必须绘制为矩形,因为它包含数据(例如颜色和宽度)并可单击。一条线做不到这一点。此外,当它转弯时,这些矩形之间也有一个三角形间隙,所以我用三角形填充它。路径必须是双倍的,因为我有另一个数据要显示在第二层上。这意味着,要为较长的路径绘制大量多边形 我在15公里的旅程中尝试过这个,它需要1000行gps数据。 当我解析文件时,它为两个层总共绘制了5000个多边

我正在开发一个应用程序,从一个文本文件中从给定的GPS位置绘制运动路径。 到目前为止,我已经成功地加载了数据并绘制了路径。 我的解决方案是,路径必须绘制为矩形,因为它包含数据(例如颜色和宽度)并可单击。一条线做不到这一点。此外,当它转弯时,这些矩形之间也有一个三角形间隙,所以我用三角形填充它。路径必须是双倍的,因为我有另一个数据要显示在第二层上。这意味着,要为较长的路径绘制大量多边形

我在15公里的旅程中尝试过这个,它需要1000行gps数据。 当我解析文件时,它为两个层总共绘制了5000个多边形

我的问题是,当它绘制多达1000个多边形的形状时,应用程序会变得迟钝,没有响应。如果我让线程休眠1秒钟,它看起来就可以了。但是速度越快,它就没有反应了

我一直在网上寻找这个解决方案,并能找到它。 仅供参考,我已经创建了另一个线程来处理文本文件。 我还缩小了问题的范围,让应用程序在不绘制多边形的情况下进行处理,并且处理过程是平滑的。 我已经读到没有其他方法来处理主线程之外的多边形

更新: 我使用Asynctask后台从文本文件中读取一行,将其解析为包含纬度、经度、值1、值2的数组。 然后大量的计算就在那里发生了。 完成每一行后,我将对象发送到onProgressUpdate,以使用标记、多段线和形状更新UI线程

这是我的任务

private class DrawPathAsync extends AsyncTask<File, Object, Void>
{

    FileInputStream is;
    BufferedReader reader;



    @Override
    protected Void doInBackground(File... params) {
        File sFile = params[0];
        Integer count;
        String line = "";
        double radius = 8; //8 meter
        double distance;

        double Heading_y;
        int kaler, gkaler;
        double apprate, gi;

        if (sFile.exists()) {
            try {
                is = new FileInputStream(sFile);
                reader = new BufferedReader(new InputStreamReader(is));
                reader.readLine(); // this will read the first line


                while ((line = reader.readLine()) != null) {

                    String[] valuesArray = line.split("\\s*,\\s*");
                    Float bearing = (float) 0;

                    Double lat = Double.parseDouble(valuesArray[1]);
                    Double lng = Double.parseDouble(valuesArray[2]);

                    LatLng latlng = new LatLng(lat, lng);
                    LatLng center = latlng;
                    apprate = Double.parseDouble(valuesArray[3]); 

                    if (apprate >=0 && apprate < 80) {
                        kaler = appcolor1; 
                    } else if (apprate >=80 && apprate < 100) { 
                        kaler = appcolor2;
                    } else if (apprate >=100 && apprate < 120) {  
                        kaler = appcolor3;
                    } else if (apprate >=120 && apprate < 140) {  
                        kaler = appcolor4;
                    } else if (apprate >=140 && apprate < 160) {  
                        kaler = appcolor5;
                    } else if (apprate >=160 && apprate <= 200) {  
                        kaler = appcolor6;
                    } else {
                        kaler = appcolor7; 
                    }


                    if (points.size()== 2) {
                        points.remove(0);
                        points.add(latlng);
                    } else {
                        points.add(latlng);
                    }


                    //recheck
                    if (points.size() == 2) {

                        distance = SphericalUtil.computeDistanceBetween(center, points.get(0));

                        LatLng pt1 = points.get(0);
                        LatLng pt2 = latlng;

                        bearing = (float) SphericalUtil.computeHeading(pt1, pt2);
                        if (bearing < 0) {
                            bearing = bearing + 360;
                        }

                        LatLng x = SphericalUtil.computeOffset(center, radius, bearing - 90);
                        LatLng y = SphericalUtil.computeOffset(center, radius, bearing + 90);


                        LatLng a = SphericalUtil.computeOffset(x, distance, bearing + 180);
                        LatLng b = SphericalUtil.computeOffset(y, distance, bearing + 180);


                        MarkerPoint mp = new MarkerPoint();
                        mp.latlng = latlng;
                        mp.bearing = bearing;

                        Rect rc = new Rect();
                        rc.a = a;
                        rc.b = b;
                        rc.x = x;
                        rc.y = y;
                        rc.kaler = kaler;
                        rc.pt2 = pt2;

                        publishProgress(mp, rc);
                    }

                    Thread.sleep(50);

                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


        }

        return null;
    }

    @Override
    protected void onProgressUpdate(Object... values) {

        MarkerPoint mp = (MarkerPoint) values[0];
        Rect rc = (Rect) values[1];



        LatLng latlng = mp.latlng;

        BitmapDescriptor icon = BitmapDescriptorFactory.fromResource(R.mipmap.pointer);
        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.position(latlng);
        markerOptions.icon(icon);
        markerOptions.rotation(mp.bearing);

        mMap.moveCamera(CameraUpdateFactory.newLatLng(latlng));
        marker1.remove();
        marker1 = mMap.addMarker(markerOptions);

        if (points.size() > 1) {
            path = mMap.addPolyline(new PolylineOptions().add(points.get(0)).add(points.get(1)).color(Color.BLUE).width(5));
            lines.add(path);
        }

        PolygonOptions options = new PolygonOptions()
                .fillColor(rc.kaler)
                .strokeWidth(0)
                .strokeColor(Color.TRANSPARENT);
        options.add(rc.x);
        options.add(rc.y);
        options.add(rc.b);
        options.add(rc.a);

        rect = mMap.addPolygon(options);
        rects.add(rect);

        if (tripoints.size() == 3) {
            tripoints.add(rc.a);
            tripoints.add(rc.b);
        } else {



            tripoints.add(rc.pt2);
            tripoints.add(rc.x);
            tripoints.add(rc.y);
        }

        //check
        //round 2, if triponts = 5 create triangle
        if (tripoints.size() == 5) {
            PolygonOptions options2 = new PolygonOptions()
                    .fillColor(rc.kaler)
                    .strokeWidth(0)
                    .strokeColor(Color.TRANSPARENT);

            options2.add(tripoints.get(0));
            options2.add(tripoints.get(2));
            options2.add(tripoints.get(4));


            t1 = mMap.addPolygon(options2);
            tris.add(t1);


            PolygonOptions options3 = new PolygonOptions()
                    .fillColor(rc.kaler)
                    .strokeWidth(0)
                    .strokeColor(Color.TRANSPARENT);

            options3.add(tripoints.get(0));
            options3.add(tripoints.get(1));
            options3.add(tripoints.get(3));



            t2 = mMap.addPolygon(options3);
            tris.add(t2);



            tripoints.clear();
            tripoints.add(rc.pt2);
            tripoints.add(rc.x);
            tripoints.add(rc.y);
        }


    }

    @Override
    protected void onPostExecute(Void result) {

    }
}
私有类DrawPathAsync扩展了AsyncTask
{
FileInputStream是;
缓冲读取器;
@凌驾
受保护的Void doInBackground(文件…参数){
文件sFile=params[0];
整数计数;
字符串行=”;
双半径=8;//8米
双倍距离;
双标题;
内特卡勒,卡勒;
双贴壁,gi;
if(sFile.exists()){
试一试{
is=新文件输入流(sFile);
reader=新的BufferedReader(新的InputStreamReader(is));
reader.readLine();//这将读取第一行
而((line=reader.readLine())!=null){
字符串[]值ray=line.split(“\\s*,\\s*”);
浮动轴承=(浮动)0;
Double lat=Double.parseDouble(valuesArray[1]);
Double lng=Double.parseDouble(valuesArray[2]);
LatLng LatLng=新LatLng(lat,lng);
LatLng中心=LatLng;
apprate=Double.parseDouble(valuesArray[3]);
如果(平均值>=0&&A平均值<80){
kaler=appcolor1;
}如果(apprate>=80&&apprate<100){
kaler=appcolor2;
}如果(apprate>=100&&apprate<120){
kaler=appcolor3;
}如果(apprate>=120&&apprate<140){
kaler=4;
}如果(apprate>=140&&apprate<160){
kaler=5;
}否则如果(通知>=160和通知1){
path=mMap.addPolyline(新的PolylineOptions().add(points.get(0)).add(points.get(1)).color(color.BLUE).width(5));
行。添加(路径);
}
polygonooptions选项=新polygonooptions()
.fillColor(rc.kaler)
.冲程宽度(0)
.strokeColor(颜色.透明);
选项。添加(rc.x);
选项。添加(rc.y);
增加(rc.b);
增加(rc.a);
rect=mMap.addPolygon(选项);
rects.add(rect);
如果(三点大小()==3){
加上三点(rc.a);
添加三点(rc.b);
}否则{
添加三点(rc.pt2);
三点相加(rc.x);
三点相加(rc.y);
}
//检查
//第2轮,如果triponts=5,则创建三角形
如果(三点大小()==5){
PolygonOptions options2=新PolygonOptions()
.fillColor(rc.kaler)
.冲程宽度(0)
.strokeColor(颜色.透明);
选项2.添加(三点。获取(0));
选项2.添加(三点获取(2));
选项2.添加(三点。获取(4));
t1=mMap.addPolygon(选项2);
三加一(t1);
polygonooptions选项3=新的polygonooptions()
.fillColor(rc.kaler)
.冲程宽度(0)
.strokeColor(颜色.透明);
选项3.添加(三点。获取(0));
选项3.添加(三点获取(1));
选项3.添加(三点获取(3));
t2=mMap.addPolygon(选项3);
三加二(t2);
三点清除();
添加三点(rc.pt2);
三点相加(rc.x);
三点相加(rc.y);
}
}
@凌驾
受保护的void onPostExecute(void结果){
}
}

希望有人能分享一些技巧和解决方案。

我已经有一段时间遇到了相同的问题,经过广泛的研究,我已经将这个问题隔离到Google Maps SDK本身。在我的案例中,一个有效的解决方案是使用A并在正确的地理坐标处绘制自定义点/线/多边形。我找到了这个库,它调用y这样做:

通过一些调整,我能够创建一个渲染线程,该线程将绘图/过滤部分从主UI线程中移除,并且仅在完成时更新地面覆盖。此外,我添加了一个简单但快速的算法来搜索当前可见的形状。通过这种方法,您可以获得一些额外的好处:

  • 仅当前视图中的对象
    /**
     * Singleton class that handle polygons showing on map.
     * This class is singleton because one instance in enough and operation that this class do it is
     * UI Thread operation so must call in ui thread.
     * This class only show polygons that are in map viewport and handle shapes.
     *
     * @version 1.3
     */
    public class PolygonRenderer implements Runnable {
    
        // Update interval in millisecond
        private final static long UPDATE_INTERVAL = 500;
    
        // Single instance of this class
        private static PolygonRenderer instance;
    
        private Thread thread;
    
        // Keep last update time in millisecond
        private long lastUpdate;
    
        // Used to stop thread
        private boolean stopFlag;
    
        // Used to pause thread
        private boolean pauseFlag;
        private final Object pauseLock;
    
        private LatLngBounds bounds;
        private float zoom;
        private List<PolygonWrapper> polygons;
        private GoogleMap map;
    
        private PolygonRenderer() {
            this.stopFlag = false;
            this.pauseFlag = false;
            this.pauseLock = new Object();
        }
    
        public static synchronized PolygonRenderer getInstance() {
            if (instance == null)
                instance = new PolygonRenderer();
            return instance;
        }
    
        /**
         * Stop polygons refreshing on map
         */
        public synchronized void stop() {
            stopFlag = true;
            if (thread != null) {
                thread.interrupt();
                thread = null;
            }
        }
    
        /**
         * Pause running thread
         */
        public synchronized void onPause() {
            pauseFlag = true;
        }
    
        /**
         * Resume thread running
         */
        public synchronized void onResume() {
            pauseFlag = false;
            pauseLock.notifyAll();
        }
    
        /**
         * Create new polygon wrapper and add it to polygons list.
         *
         * @param activity context activity of map
         * @param id       id of polygon
         * @param geometry data of polygon such as points
         * @param polygons list af all polygons
         * @see PolygonWrapper for more info about polygon wrapper.
         */
        public synchronized void createPolygons(Activity activity, String id, String geometry, List<PolygonWrapper> polygons) {
            try {
    
                // Read polygon data (coordinates)
                WKTReader wkt = new WKTReader();
                if (geometry.contains("MULTIPOLYGON")) {
                    org.locationtech.jts.geom.MultiPolygon multiPolygon = (org.locationtech.jts.geom.MultiPolygon) wkt.read(geometry);
    
                    // Gets each polygon of a multipolygon
                    for(int i = 0; i < multiPolygon.getNumGeometries(); i++) {
    
                        org.locationtech.jts.geom.Polygon polygon = (org.locationtech.jts.geom.Polygon) multiPolygon.getGeometryN(i);
    
                        // Create polygon options
                        PolygonOptions options = new PolygonOptions();
                        options.strokeWidth(8);
                        options.clickable(true);
    
                        // Gets each polygon outer coordinates
                        ArrayList<LatLng> outer = new ArrayList<>();
                        Coordinate[] outerCoordinates = polygon.getExteriorRing().getCoordinates();
                        for (Coordinate outerCoordinate : outerCoordinates)
                            outer.add(new LatLng(outerCoordinate.y, outerCoordinate.x));
                        options.addAll(outer);
    
                        // Getting each polygon interior coordinates (hole) if they exist
                        if(polygon.getNumInteriorRing() > 0){
                            for(int j = 0; j < polygon.getNumInteriorRing(); j++){
                                ArrayList<LatLng> inner = new ArrayList<>();
                                Coordinate[] innerCoordinates = polygon.getInteriorRingN(j).getCoordinates();
                                for (Coordinate innerCoordinate : innerCoordinates)
                                    inner.add(new LatLng(innerCoordinate.y, innerCoordinate.x));
                                options.addHole(inner);
                            }
                        }
    
                        // Create and add polygon wrapper
                        polygons.add(new PolygonWrapper(activity, id, options, PolygonWrapper.Behavior.PART_SHOWING));
                    }
                } else {
                    org.locationtech.jts.geom.Polygon polygon = (org.locationtech.jts.geom.Polygon) wkt.read(geometry);
    
                    // Create polygon options
                    PolygonOptions options = new PolygonOptions();
                    options.strokeWidth(8);
                    options.clickable(true);
    
                    // Gets polygon outer coordinates
                    ArrayList<LatLng> outer = new ArrayList<>();
                    Coordinate[] outerCoordinates = polygon.getExteriorRing().getCoordinates();
                    for (Coordinate outerCoordinate : outerCoordinates)
                        outer.add(new LatLng(outerCoordinate.y, outerCoordinate.x));
                    options.addAll(outer);
    
                    // Getting polygon interior coordinates (hole) if they exist
                    if(polygon.getNumInteriorRing() > 0){
                        for(int j = 0; j < polygon.getNumInteriorRing(); j++){
                            ArrayList<LatLng> inner = new ArrayList<>();
                            Coordinate[] innerCoordinates = polygon.getInteriorRingN(j).getCoordinates();
                            for (Coordinate innerCoordinate : innerCoordinates)
                                inner.add(new LatLng(innerCoordinate.y, innerCoordinate.x));
                            options.addHole(inner);
                        }
                    }
    
                    // Create and add polygon wrapper
                    polygons.add(new PolygonWrapper(activity, id, options, PolygonWrapper.Behavior.PART_SHOWING));
                }
    
            } catch (org.locationtech.jts.io.ParseException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * Update visible polygons on map based on locating in map viewport.
         * Also map zoom is important in showing polygons, because of polygons count on map in low zooms.
         * We remove some very small polygons in low zoom.
         * This operations is require to prevent app not responding when polygons are too many.
         * Polygons that are not in viewport will be remove from map.
         * This method must be call in onCameraMove event to get map new bounds and zoom.
         * Operations will be done in new thread. Thread change polygons visibility continuously.
         *
         * @param map      map that polygons must be shown on it
         * @param polygons list of all polygons
         */
        public synchronized void updatePolygons(GoogleMap map, List<PolygonWrapper> polygons) {
    
            // Limit update interval
            long time = SystemClock.elapsedRealtime();
            if (time - lastUpdate < UPDATE_INTERVAL)
                return;
    
            // Update last update time
            lastUpdate = time;
    
            // Bounds and zoom should get in ui thread. so we get them out of thread
            this.bounds = map.getProjection().getVisibleRegion().latLngBounds;
            this.zoom = map.getCameraPosition().zoom;
    
            // We have only one thread and if it is created so we don't need recreate it
            if (thread != null)
                return;
    
            // Create and run thread
            this.map = map;
            this.polygons = polygons;
            this.stopFlag = false;
            thread = new Thread(this);
            thread.start();
        }
    
        @Override
        public void run() {
            while (!stopFlag) {
    
                // Call by try-catch to prevent unwanted exception and thread stopping
                try {
    
                    // Pause implementation
                    synchronized (pauseLock) {
                        while (pauseFlag) {
                            try {
                                pauseLock.wait();
                            } catch (InterruptedException ignored) {
                            }
                        }
                    }
    
                    // Update visible polygons on map based on map viewport
                    for (PolygonWrapper polygon : polygons) {
    
                        // Remove polygons that are invisible in given zoom from map
                        if (isVisibleWithZoom(polygon, zoom)) {
                            if (polygon.isAddedToMap()) {
                                polygon.removeFromMap();
                                sleep();
                            }
                            continue;
                        }
    
                        // Hide out of map viewport polygons
                        if (polygon.isWithin(bounds) && !polygon.isAddedToMap()) {
                            polygon.addToMap(map);
                            sleep();
                        } else if (!polygon.isWithin(bounds)) {
                            polygon.removeFromMap();
                            sleep();
                        }
                    }
                } catch (Exception ignored) {
                }
            }
        }
    
        private boolean isVisibleWithZoom(PolygonWrapper polygon, float zoom) {
    
            // Compute area of polygon
            double area = SphericalUtil.computeArea(polygon.getOptions().getPoints());
    
            return (
                    (zoom <= 11 && area <= 1000) ||                         // Don't show polygons with area <= 1000 when zoom is <= 11 (map bounds has great area)
                            (zoom > 11 && zoom <= 12 && area <= 500) ||     // Don't show polygons with area <= 500 when zoom is between 11 and 12
                            (zoom > 12 && zoom <= 13 && area <= 250) ||     // Don't show polygons with area <= 250 when zoom is between 12 and 13
                            (zoom > 13 && zoom <= 13.5 && area <= 200) ||   // Don't show polygons with area <= 200 when zoom is between 13 and 13.5
                            (zoom > 13.5 && zoom <= 14 && area <= 150) ||   // Don't show polygons with area <= 150 when zoom is between 13.5 and 14 (map bounds has small area)
                            (zoom > 14 && zoom <= 14.5 && area <= 100)      // Don't show polygons with area <= 100 when zoom is between 14 and 14.5 (map bounds has small area)
            );
        }
    
        /**
         * Thread sleep allow ui thread to show views and doesn't hang up.
         * Call this method everywhere ui thread action is performing.
         */
        private void sleep() throws InterruptedException {
            Thread.sleep(8);
        }
    }
    
    /**
     * Wrapper class for polygon.
     * See https://stackoverflow.com/questions/36439031/determine-if-polygon-is-within-map-bounds for more info.
     *
     * @version 1.1
     * */
    public class PolygonWrapper {
    
        private final String id;
        private final Behavior behavior;
        private final LatLng northWest, northEast, southEast, southWest;
        private final Activity activity;
    
        private Polygon polygon;
        private PolygonOptions options;
    
        public void addToMap(GoogleMap map) {
            activity.runOnUiThread(() -> {
                if (isAddedToMap()) removeFromMap();
                polygon = map.addPolygon(options);
            });
        }
    
        public void removeFromMap() {
            activity.runOnUiThread(() -> {
                if (isAddedToMap()) {
                    polygon.remove();
                    polygon = null;
                }
            });
        }
    
        public PolygonWrapper(Activity activity, String id, PolygonOptions options, Behavior behavior) {
            this.activity = activity;
            this.id = id;
            this.options = options;
            this.behavior = behavior;
    
            Double north = null, west = null, south = null, east = null;
            for (LatLng latLng : options.getPoints()) {
                if (north == null || latLng.latitude > north)
                    north = latLng.latitude;
    
                if (west == null || latLng.longitude < west)
                    west = latLng.longitude;
    
                if (south == null || latLng.latitude < south)
                    south = latLng.latitude;
    
                if (east == null || latLng.longitude > east)
                    east = latLng.longitude;
            }
            northWest = new LatLng(north, west);
            northEast = new LatLng(north, east);
            southEast = new LatLng(south, east);
            southWest = new LatLng(south, west);
        }
    
        public String getId() {
            return id;
        }
    
        public PolygonOptions getOptions() {
            return options;
        }
    
        public Polygon getPolygon() {
            return polygon;
        }
    
        public PolygonOptions buildBordersRectPolygonOptions() {
            final PolygonOptions rvalue = new PolygonOptions();
            rvalue.add(northWest);
            rvalue.add(northEast);
            rvalue.add(southEast);
            rvalue.add(southWest);
            rvalue.add(northWest);
            rvalue.fillColor(0x6A00FFFF);
            rvalue.strokeColor(0x6AFF0000);
            rvalue.strokeWidth(1f);
            return rvalue;
        }
    
        public boolean isWithin(LatLngBounds bounds) {
            boolean within = false;
            switch (behavior) {
                case FULL_SHOWING:
                    if (bounds.contains(northWest) && bounds.contains(southEast))
                        within = true;
                    break;
                case PART_SHOWING:
                    if (bounds.contains(northWest)
                            || bounds.contains(southEast)
                            || bounds.contains(northEast)
                            || bounds.contains(southWest)) {
                        within = true;
                    } else if (northEast.latitude > bounds.southwest.latitude
                            && northEast.longitude > bounds.southwest.longitude
                            && southWest.latitude < bounds.northeast.latitude
                            && southWest.longitude < bounds.northeast.longitude) {
                        within = true;
                    }
                    break;
            }
            return within;
        }
    
        public boolean isAddedToMap() {
            return polygon != null;
        }
    
        public enum Behavior {
            FULL_SHOWING, PART_SHOWING
        }
    }
    
    map.setOnCameraMoveListener(() -> {
         // Update visible polygons on map
         PolygonRenderer.getInstance().updatePolygons(map, polygons);
    });
    
    @Override
    protected void onStop() {
        // Stop polygons renderer class on activity stop
        PolygonRenderer.getInstance().stop();
    
        super.onStop();
    }