在多边形上查找用户位置的最近点 [英] Find the closest point on polygon to user location

查看:184
本文介绍了在多边形上查找用户位置的最近点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我想将多边形转换为Geofence以检查用户到多边形之间的距离该区域给予用户准确的信息。

我该怎么做?

这是MapsActivity

  public class MapsActivity扩展FragmentActivity实现OnMapReadyCallback,LocationListener,MinimumDistanceTask.GetMinimumDistanceListener {
private GoogleMap mMap;
私人LocationManager管理器;
私人双拉特,lng;
私有KmlLayer图层;
私人LatLng latLngTest;
private boolean contains = false;
private ArrayList< LatLng> outerBoundary;

@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
//获取SupportMapFragment并在地图准备好使用时得到通知。
SupportMapFragment mapFragment =(SupportMapFragment)getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
manager =(LocationManager)getSystemService(LOCATION_SERVICE);


@Override
protected void onResume(){
super.onResume();
字符串提供者= LocationManager.GPS_PROVIDER;

//每秒获取用户位置
尝试{
manager.requestLocationUpdates(provider,1000,0,this);
} catch(SecurityException e){

}
}

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

@覆盖
public void onLocationChanged(位置位置){
//在创建新位置前清除地图
mMap.clear();
尝试{
//载入kml文件
layer = new KmlLayer(mMap,R.raw.polygon_layer,this);
layer.addLayerToMap();

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

lat = location.getLatitude();
ng = location.getLongitude();
latLngTest = new LatLng(lat,lng);
//在用户位置添加一个标记
LatLng userLocation = new LatLng(latLngTest.latitude,latLngTest.longitude);
mMap.addMarker(new MarkerOptions()。position(userLocation).title(you are here));
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(userLocation,15));

//检查多边形中的用户
boolean inside = ifUserInside();

if(inside){
Toast.makeText(MapsActivity.this,你在多边形中,Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MapsActivity.this,你在多边形之外,Toast.LENGTH_SHORT).show();
//创建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){
//检查结果是否正确
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);


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

}

@Override
public void onProviderEnabled(String provider){

}

@Override
public void onProviderDisabled(String provider){ (KmlContainer容器:图层)的


$ b公共布尔ifUserInside(){
if(layer.getContainers()!= null){
。 getContainers()){
if(container.getPlacemarks()!= null){
for(KmlPlacemark placemark:container.getPlacemarks()){
contains = false; (placemark.getGeometry()instanceof KmlPolygon){
KmlPolygon polygon =(KmlPolygon)placemark.getGeometry();

if(placemark.getGeometry

//获取外部边界并检查测试位置是否位于
内部outerBoundary = polygon.getOuterBoundaryCoordinates();
contains = PolyUtil.containsLocation(latLngTest,outerBoundary,true);



if(contains){
//获取内部边界并检查测试位置是否位于
内ArrayList< ArrayList< LatLng>> ; innerBoundaries = polygon.getInnerBoundaryCoordinates();
if(innerBoundaries!= null){
for(ArrayList< LatLng> innerBoundary:innerBoundaries){
//如果测试位置位于一个孔中,则该多边形不包含该位置
if(PolyUtil.containsLocation(latLngTest,innerBoundary,true)){
contains = false;

}
}
}
}
}
}
}
}
}
返回包含;
}

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];

尝试{
网址url =新网址(地址);
connection =(HttpsURLConnection)url.openConnection();
if(connection.getResponseCode()!= HttpURLConnection.HTTP_OK){
return服务器出错;
}

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

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

return builder.toString();
}

@Override
protected void onPostExecute(String s){
//获取折线字符串
String polygonPoints =;

尝试{
JSONObject对象= 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);

}
//将字符串转换为折线;
ArrayList< LatLng> a = new ArrayList<>(decodePolyPoints(polygonPoints));
//将折线添加到地图
mMap.addPolyline(new PolylineOptions()。addAll(a).width(10).color(Color.BLUE));

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

//将字符串转换为多段线的方法
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 + = 5;
} while(b> = 0x1f);
lat + =(result& 1)!= 0? 〜(结果>> 1):(结果>> 1);

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

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

返回路径;




$ b

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

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

private int closeLocation;
// private String points;
private GetMinimumDistanceListener监听器;

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;

字符串地址= params [0];

尝试{
网址url =新网址(地址);
connection =(HttpsURLConnection)url.openConnection();
if(connection.getResponseCode()!= HttpURLConnection.HTTP_OK){
return -1;
}

reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
字符串行; ((line = reader.readLine())!= null){
builder.append(line);
while
}
///获取json数据
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;


$ b} 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);


$ / code $ / pre

非常感谢:

解决方案

您可以使用类似下面的函数来计算由 List< LatLng> 和给定的 LatLng



它使用 PolyUtil.distanceToLine 来自 Google地图Android API实用程序库来计算测试 LatLng 和列表的每个段之间的距离,以及基于 distanceToLine 方法来自 https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/PolyUtil.java 来计算某个细分受众群的投影。

  private LatLng findNearestPoint(LatLng test,List< LatLng>目标){
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(距离== -1 || currentDistance <距离){
distance = currentDistance;
minimumDistancePoint = findNearestPoint(test,point,target.get(segmentPoint));
}
}

返回minimumDistancePoint;
}

/ **
*基于
的`distanceToLine`方法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;
}

返回新的LatLng(start.latitude +(u *(end.latitude - start.latitude)),
start.longitude +(u *(end.longitude - start.longitude)));


}

您可以使用以下代码测试它:

 列表< LatLng> points = new ArrayList<>(); 
points.add(新LatLng(2,2));
points.add(新LatLng(4,2));
points.add(新LatLng(4,4));
points.add(new LatLng(2,4));
points.add(新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


I have an app that find the shortest distance between my user to a polygon.

I want to convert the polygon to Geofence to check the distance between the user to the area to give mor accurate information to the user.

how can I do that?

this is the 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;
    }
}

This is my AsyncTask to get the minimum distance point

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);
    }
}

thanks a lot :)

解决方案

You can use a function like the following to calculate the nearest point from polygon defined by a List<LatLng> and a given LatLng.

It uses the PolyUtil.distanceToLine from the Google Maps Android API Utility Library to compute the distance between the test LatLng and every segment of the list, and a method based on the distanceToLine method from https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/PolyUtil.java to compute the projection of a point on a segment.

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)));


}

You can test it with the following code:

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

这篇关于在多边形上查找用户位置的最近点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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