ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

android – 在多边形上找到最接近用户位置的点

2019-07-22 13:24:56  阅读:205  来源: 互联网

标签:android-geofence android google-maps polygon


我有一个应用程序,找到我的用户与多边形之间的最短距离.

我想将多边形转换为Geofence以检查用户与区域之间的距离,以便向用户提供准确的信息.

我怎样才能做到这一点?

这是MapsActivity

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, LocationListener, MinimumDistanceTask.GetMinimumDistanceListener {
    private GoogleMap mMap;
    private  LocationManager manager;
    private double lat, lng;
    private KmlLayer layer;
    private LatLng latLngTest;
    private  boolean contains = false;
    private  ArrayList<LatLng> outerBoundary;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
        manager = (LocationManager) getSystemService(LOCATION_SERVICE);
    }

    @Override
    protected void onResume() {
        super.onResume();
        String provider = LocationManager.GPS_PROVIDER;

        //take the user location every second
        try {
            manager.requestLocationUpdates(provider, 1000, 0, this);
        }catch (SecurityException e){

        }
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
    }

    @Override
    public void onLocationChanged(Location location) {
        //clear map before create new location
        mMap.clear();
        try {
            //load the kml file
            layer = new KmlLayer(mMap, R.raw.polygon_layer, this);
            layer.addLayerToMap();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        }

        lat = location.getLatitude();
        lng = location.getLongitude();
        latLngTest = new LatLng(lat,lng);
        // Add a marker in user location
        LatLng userLocation = new LatLng(latLngTest.latitude, latLngTest.longitude);
        mMap.addMarker(new MarkerOptions().position(userLocation).title("you are here"));
        mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(userLocation, 15));

        //check if the user in the polygon
        boolean inside = ifUserInside();

        if(inside){
            Toast.makeText(MapsActivity.this, "you are in the polygon", Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(MapsActivity.this, "you are outside the polygon", Toast.LENGTH_SHORT).show();
            //create the string address for the url
            String address = "";
            for (int i = 0; i < outerBoundary.size(); i++) {
                address += (outerBoundary.get(i).toString() + "|");
                address = address.replace("lat/lng:", "");
                address = address.replace(" ", "");
                address = address.replace("(", "");
                address = address.replace(")", "");
            }
            MinimumDistanceTask task = new MinimumDistanceTask(this);
            task.execute("https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins="+latLngTest.latitude+ "," + latLngTest.longitude
                    + "&destinations=" + address + "&mode=walking");
        }

    }

    @Override
    public void getMinimumDistance(int closeLocation) {
        //check if you get results properly
        if(closeLocation != -1) {
            GetDirection direction = new GetDirection();
            direction.execute("https://maps.googleapis.com/maps/api/directions/json?origin=" + latLngTest.latitude + "," + latLngTest.longitude
                    + "&destination=" + outerBoundary.get(closeLocation).latitude + "+" + outerBoundary.get(closeLocation).longitude);
        }
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }

    public boolean ifUserInside(){
        if (layer.getContainers() != null) {
            for (KmlContainer container : layer.getContainers()) {
                if (container.getPlacemarks() != null) {
                    for (KmlPlacemark placemark : container.getPlacemarks()) {
                        contains = false;

                        if (placemark.getGeometry() instanceof KmlPolygon) {
                            KmlPolygon polygon = (KmlPolygon) placemark.getGeometry();

                            // Get the outer boundary and check if the test location lies inside
                            outerBoundary = polygon.getOuterBoundaryCoordinates();
                            contains = PolyUtil.containsLocation(latLngTest, outerBoundary, true);



                            if (contains) {
                                // Get the inner boundaries and check if the test location lies inside
                                ArrayList<ArrayList<LatLng>> innerBoundaries = polygon.getInnerBoundaryCoordinates();
                                if (innerBoundaries != null) {
                                    for (ArrayList<LatLng> innerBoundary : innerBoundaries) {
                                        // If the test location lies in a hole, the polygon doesn't contain the location
                                        if (PolyUtil.containsLocation(latLngTest, innerBoundary, true)) {
                                            contains = false;

                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return contains;
    }

    public class GetDirection extends AsyncTask<String , Void, String> {
        HttpsURLConnection connection = null;
        BufferedReader reader = null;
        StringBuilder builder = new StringBuilder();
        @Override
        protected String doInBackground(String... params) {
            String address = params[0];

            try {
                URL url = new URL(address);
                connection = (HttpsURLConnection) url.openConnection();
                if(connection.getResponseCode() != HttpURLConnection.HTTP_OK){
                    return "Error from server";
                }

                reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String line;
                while ((line = reader.readLine()) != null){
                    builder.append(line);
                }

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return builder.toString();
        }

        @Override
        protected void onPostExecute(String s) {
        //get the polyline string
            String polygonPoints = "";

            try {
                JSONObject object = new JSONObject(s);
                JSONArray array = object.getJSONArray("routes");
                for (int i = 0; i < array.length(); i++) {
                    JSONObject arrObj1 = array.getJSONObject(i);
                    JSONObject points = arrObj1.getJSONObject("overview_polyline");
                    polygonPoints = points.getString("points");

                }
                //convert the string to polyline;
                ArrayList<LatLng> a = new ArrayList<>(decodePolyPoints(polygonPoints));
                //add polyline to the map
                mMap.addPolyline(new PolylineOptions().addAll(a).width(10).color(Color.BLUE));

            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }

    //the method that convert the string to polyline
    public static ArrayList<LatLng> decodePolyPoints(String encodedPath){
        int len = encodedPath.length();

        final ArrayList<LatLng> path = new ArrayList<LatLng>();
        int index = 0;
        int lat = 0;
        int lng = 0;

        while (index < len) {
            int result = 1;
            int shift = 0;
            int b;
            do {
                b = encodedPath.charAt(index++) - 63 - 1;
                result += b << shift;
                shift += 5;
            } while (b >= 0x1f);
            lat += (result & 1) != 0 ? ~(result >> 1) : (result >> 1);

            result = 1;
            shift = 0;
            do {
                b = encodedPath.charAt(index++) - 63 - 1;
                result += b << shift;
                shift += 5;
            } while (b >= 0x1f);
            lng += (result & 1) != 0 ? ~(result >> 1) : (result >> 1);

            path.add(new LatLng(lat * 1e-5, lng * 1e-5));
        }

        return path;
    }
}

这是我的AsyncTask来获得最小距离点

public class MinimumDistanceTask extends AsyncTask<String, Void, Integer>{

    private int closeLocation;
    // private String points;
    private GetMinimumDistanceListener listener;

    public MinimumDistanceTask(GetMinimumDistanceListener listener){
        // this.points = points;
        this.listener = listener;
    }

    @Override
    protected Integer doInBackground(String... params) {
        HttpsURLConnection connection = null;
        BufferedReader reader = null;
        StringBuilder builder = new StringBuilder();
        int minimumDis = -1;

            String address = params[0];

            try {
                URL url = new URL(address);
                connection = (HttpsURLConnection) url.openConnection();
                if(connection.getResponseCode() != HttpURLConnection.HTTP_OK){
                    return -1;
                }

                reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String line;
                while ((line = reader.readLine()) != null){
                    builder.append(line);
                }
            ///get the json data
                JSONObject jsonObject1 = new JSONObject(builder.toString());
                JSONArray points = jsonObject1.getJSONArray("rows");
                JSONObject jsonObject2 = points.getJSONObject(0);
                JSONArray elements = jsonObject2.getJSONArray("elements");
                for (int i = 0; i < elements.length(); i++) {
                    JSONObject jsonObject3 = elements.getJSONObject(i);
                    JSONObject distance = jsonObject3.getJSONObject("distance");
                    if( distance.getInt("value") < minimumDis || minimumDis == -1) {
                        minimumDis = distance.getInt("value");
                        closeLocation = i;
                    }
                }

            } catch (MalformedURLException | JSONException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        return closeLocation;
        }

    @Override
    protected void onPostExecute(Integer closeLocation) {
            listener.getMinimumDistance(closeLocation);

    }

    public interface GetMinimumDistanceListener{
        void getMinimumDistance(int closeLocation);
    }
}

非常感谢 :)

解决方法:

您可以使用如下所示的函数来计算由List< LatLng>定义的多边形中的最近点.和给定的LatLng.

它使用Google Maps Android API Utility Library中的PolyUtil.distanceToLine计算测试LatLng与列表的每个段之间的距离,以及基于https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/PolyUtil.java的distanceToLine方法计算段上点的投影的方法.

private LatLng findNearestPoint(LatLng test, List<LatLng> target) {
    double distance = -1;
    LatLng minimumDistancePoint = test;

    if (test == null || target == null) {
        return minimumDistancePoint;
    }

    for (int i = 0; i < target.size(); i++) {
        LatLng point = target.get(i);

        int segmentPoint = i + 1;
        if (segmentPoint >= target.size()) {
            segmentPoint = 0;
        }

        double currentDistance = PolyUtil.distanceToLine(test, point, target.get(segmentPoint));
        if (distance == -1 || currentDistance < distance) {
            distance = currentDistance;
            minimumDistancePoint = findNearestPoint(test, point, target.get(segmentPoint));
        }
    }

    return minimumDistancePoint;
}

/**
 * Based on `distanceToLine` method from
 * https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/PolyUtil.java
 */
private LatLng findNearestPoint(final LatLng p, final LatLng start, final LatLng end) {
    if (start.equals(end)) {
        return start;
    }

    final double s0lat = Math.toRadians(p.latitude);
    final double s0lng = Math.toRadians(p.longitude);
    final double s1lat = Math.toRadians(start.latitude);
    final double s1lng = Math.toRadians(start.longitude);
    final double s2lat = Math.toRadians(end.latitude);
    final double s2lng = Math.toRadians(end.longitude);

    double s2s1lat = s2lat - s1lat;
    double s2s1lng = s2lng - s1lng;
    final double u = ((s0lat - s1lat) * s2s1lat + (s0lng - s1lng) * s2s1lng)
            / (s2s1lat * s2s1lat + s2s1lng * s2s1lng);
    if (u <= 0) {
        return start;
    }
    if (u >= 1) {
        return end;
    }

    return new LatLng(start.latitude + (u * (end.latitude - start.latitude)),
            start.longitude + (u * (end.longitude - start.longitude)));


}

您可以使用以下代码进行测试:

List<LatLng> points = new ArrayList<>();
points.add(new LatLng(2, 2));
points.add(new LatLng(4, 2));
points.add(new LatLng(4, 4));
points.add(new LatLng(2, 4));
points.add(new LatLng(2, 2));

LatLng testPoint = new LatLng(3, 0);

LatLng nearestPoint = findNearestPoint(testPoint, points);
Log.e("NEAREST POINT: ", "" + nearestPoint); // lat/lng: (3.0,2.0)
Log.e("DISTANCE: ", "" + SphericalUtil.computeDistanceBetween(testPoint, nearestPoint)); // 222085.35856591124

标签:android-geofence,android,google-maps,polygon
来源: https://codeday.me/bug/20190722/1503147.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有