在多边形上查找用户位置的最近点 [英] Find the closest point on polygon to user location
问题描述
我想将多边形转换为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> $定义的多边形的最近点c $ c>和给定的 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屋!