Java 多边形触摸检测谷歌地图API V2
我正试图找出最好的方法,我有一张画有一个Java 多边形触摸检测谷歌地图API V2,java,android,coordinates,google-maps-android-api-2,point-in-polygon,Java,Android,Coordinates,Google Maps Android Api 2,Point In Polygon,我正试图找出最好的方法,我有一张画有一个多边形的地图。因为谷歌地图API V2似乎没有多边形上的触摸检测功能。我想知道是否有可能检测到接触点是否在多边形内?如果是这样,我的主要目标是在地图上勾勒出一个状态,当用户点击该状态时,它将在自定义视图中显示更多细节。到目前为止,我能够捕获地图的MapOnClick,但是当用户点击Polygon内部时,我希望在Toast上设置Polygon.getID()。我是新手,所以如果我不够清楚,我道歉 googleMap.setOnMapClickListener
多边形的地图。因为谷歌地图API V2似乎没有多边形上的触摸检测功能。我想知道是否有可能检测到接触点是否在多边形内?如果是这样,我的主要目标是在地图上勾勒出一个状态,当用户点击该状态时,它将在自定义视图中显示更多细节。到目前为止,我能够捕获地图的MapOnClick
,但是当用户点击Polygon
内部时,我希望在Toast
上设置Polygon.getID()
。我是新手,所以如果我不够清楚,我道歉
googleMap.setOnMapClickListener(new OnMapClickListener()
{
public void onMapClick(LatLng point)
{
boolean checkPoly = true;
Toast.makeText(MainActivity.this,"The Location is outside of the Area", Toast.LENGTH_LONG).show();
}
});
}
}
catch (Exception e) {
Log.e("APP","Failed", e);
}
好的,这就是我到目前为止所做的
private boolean rayCastIntersect(LatLng tap, LatLng vertA, LatLng vertB) {
double aY = vertA.latitude;
double bY = vertB.latitude;
double aX = vertA.longitude;
double bX = vertB.longitude;
double pY = tap.latitude;
double pX = tap.longitude;
if (aY > bY) {
aX = vertB.longitude;
aY = vertB.latitude;
bX = vertA.longitude;
bX = vertA.latitude;
}
System.out.println("aY: "+aY+" aX : "+aX);
System.out.println("bY: "+bY+" bX : "+bX);
if (pX < 0) pX += 360;
if (aX < 0) aX += 360;
if (bX < 0) bX += 360;
if (pY == aY || pY == bY) pY += 0.00000001;
if ((pY > bY || pY < aY) || (pX > Math.max(aX, bX))) return false;
if (pX < Math.min(aX, bX))
return true;
// }
double m = (aX != bX) ? ((bY - aY) / (bX - aX)) : aX;
double bee = (aX != pX) ? ((pY - aY) / (pX - aX)) : aX;
double x = (pY - bee) / m;
return x > pX;
}
private boolean rayCastIntersect(LatLng tap、LatLng vertA、LatLng vertB){
双aY=垂直纬度;
double bY=垂直纬度;
双轴=垂直经度;
双bX=垂直经度;
双pY=抽头纬度;
双pX=抽头经度;
如果(aY>bY){
aX=垂直经度;
aY=垂直纬度;
bX=垂直经度;
bX=垂直纬度;
}
系统输出打印项次(“aY:+aY+”aX:+aX);
System.out.println(“bY:”+bY+“bX:”+bX);
如果(pX<0)pX+=360;
如果(aX<0)aX+=360;
如果(bX<0)bX+=360;
如果(pY==aY | | pY==bY)pY+=0.00000001;
if((pY>bY | | pYMath.max(aX,bX)))返回false;
如果(pX<数学最小值(aX,bX))
返回true;
// }
双m=(aX!=bX)?((bY-aY)/(bX-aX)):aX;
双蜜蜂=(aX!=pX)?((pY-aY)/(pX-aX)):aX;
双x=(pY-bee)/m;
返回x>pX;
}
}
我遇到的问题是,在每个多边形到达另一个多边形之前,触摸在每个多边形的左侧都是真实的。我的算法有什么问题会导致这个问题?任何帮助都将不胜感激。您试图解决的问题是测试
要帮助可视化光线投射的概念:
在一张纸上画一个多边形。然后,从任意点开始,在页面右侧绘制一条直线。如果直线与多边形相交的次数为奇数,则表示起点位于多边形内部。
那么,如何在代码中实现这一点呢
多边形由顶点列表组成:ArrayList顶点
。您需要单独查看每个线段
,并查看光线是否与之相交
private boolean isPointInPolygon(Geopoint tap, ArrayList<Geopoint> vertices) {
int intersectCount = 0;
for(int j=0; j<vertices.size()-1; j++) {
if( rayCastIntersect(tap, vertices.get(j), vertices.get(j+1)) ) {
intersectCount++;
}
}
return (intersectCount%2) == 1); // odd = inside, even = outside;
}
private boolean rayCastIntersect(Geopoint tap, Geopoint vertA, Geopoint vertB) {
double aY = vertA.getLatitude();
double bY = vertB.getLatitude();
double aX = vertA.getLongitude();
double bX = vertB.getLongitude();
double pY = tap.getLatitude();
double pX = tap.getLongitude();
if ( (aY>pY && bY>pY) || (aY<pY && bY<pY) || (aX<pX && bX<pX) ) {
return false; // a and b can't both be above or below pt.y, and a or b must be east of pt.x
}
double m = (aY-bY) / (aX-bX); // Rise over run
double bee = (-aX) * m + aY; // y = mx + b
double x = (pY - bee) / m; // algebra is neat!
return x > pX;
}
private boolean isPointInPolygon(地质点点击、阵列列表顶点){
int int intersectCount=0;
对于(int j=0;jpY&&bY>pY)| |(aY只是为了一致性-当用户点击多边形(或其他覆盖)时,不会调用onmaplick,javadoc中提到了它
我做了一个变通办法,在MapFragment处理taps事件之前拦截它们,并将点投影到地图坐标,并检查该点是否在任何多边形内,如其他答案中所建议的
查看更多详细信息谷歌地图支持库现在有一个静态方法为您执行此检查:
PolyUtil.containsLocation(LatLng point, List<LatLng>polygon, boolean geodesic);
PolyUtil.containsLocation(LatLng点、列表多边形、布尔测地线);
尽管文档在指南中没有明确提及,但方法就在那里
这里有一个完整的工作示例,可以了解多边形上是否发生了触摸。有些答案比需要的更复杂。此解决方案使用“android maps utils”
//编译'com.google.maps.android:android-maps-utils:0.3.4'
private ArrayList polygonList=新的ArrayList();
私有void addMyPolygons(){
polygonooptions选项=新的polygonooptions();
//TODO:根据需要制作多边形
多边形=谷歌地图。添加多边形(选项);
添加(多边形);
}
@凌驾
公共空区(停车点){
布尔包含=假;
用于(多边形p:多边形列表){
contains=PolyUtil.containsLocation(point,p.getPoints(),false);
如果(包含)断裂;
}
Toast.makeText(getActivity(),“单击多边形?”
+包含,Toast.LENGTH_SHORT).show();
}
@凌驾
已保存的受保护的空(视图、捆绑包保存状态){
setOnMapClickListener(this);
addMyPolygons();
}
虽然user1504495的回答和我使用的一样简短。但是不要使用整体,而是使用这种方法
从活动类中相应地传递参数:
if (area.containsLocation(Touchablelatlong, listLatlong, true))
isMarkerINSide = true;
else
isMarkerINSide = false;
并将以下内容放在一个单独的类中:
/**
* Computes whether the given point lies inside the specified polygon.
* The polygon is always cosidered closed, regardless of whether the last point equals
* the first or not.
* Inside is defined as not containing the South Pole -- the South Pole is always outside.
* The polygon is formed of great circle segments if geodesic is true, and of rhumb
* (loxodromic) segments otherwise.
*/
public static boolean containsLocation(LatLng point, List<LatLng> polygon, boolean geodesic) {
final int size = polygon.size();
if (size == 0) {
return false;
}
double lat3 = toRadians(point.latitude);
double lng3 = toRadians(point.longitude);
LatLng prev = polygon.get(size - 1);
double lat1 = toRadians(prev.latitude);
double lng1 = toRadians(prev.longitude);
int nIntersect = 0;
for (LatLng point2 : polygon) {
double dLng3 = wrap(lng3 - lng1, -PI, PI);
// Special case: point equal to vertex is inside.
if (lat3 == lat1 && dLng3 == 0) {
return true;
}
double lat2 = toRadians(point2.latitude);
double lng2 = toRadians(point2.longitude);
// Offset longitudes by -lng1.
if (intersects(lat1, lat2, wrap(lng2 - lng1, -PI, PI), lat3, dLng3, geodesic)) {
++nIntersect;
}
lat1 = lat2;
lng1 = lng2;
}
return (nIntersect & 1) != 0;
}
/**
* Wraps the given value into the inclusive-exclusive interval between min and max.
* @param n The value to wrap.
* @param min The minimum.
* @param max The maximum.
*/
static double wrap(double n, double min, double max) {
return (n >= min && n < max) ? n : (mod(n - min, max - min) + min);
}
/**
* Returns the non-negative remainder of x / m.
* @param x The operand.
* @param m The modulus.
*/
static double mod(double x, double m) {
return ((x % m) + m) % m;
}
/**
* Computes whether the vertical segment (lat3, lng3) to South Pole intersects the segment
* (lat1, lng1) to (lat2, lng2).
* Longitudes are offset by -lng1; the implicit lng1 becomes 0.
*/
private static boolean intersects(double lat1, double lat2, double lng2,
double lat3, double lng3, boolean geodesic) {
// Both ends on the same side of lng3.
if ((lng3 >= 0 && lng3 >= lng2) || (lng3 < 0 && lng3 < lng2)) {
return false;
}
// Point is South Pole.
if (lat3 <= -PI/2) {
return false;
}
// Any segment end is a pole.
if (lat1 <= -PI/2 || lat2 <= -PI/2 || lat1 >= PI/2 || lat2 >= PI/2) {
return false;
}
if (lng2 <= -PI) {
return false;
}
double linearLat = (lat1 * (lng2 - lng3) + lat2 * lng3) / lng2;
// Northern hemisphere and point under lat-lng line.
if (lat1 >= 0 && lat2 >= 0 && lat3 < linearLat) {
return false;
}
// Southern hemisphere and point above lat-lng line.
if (lat1 <= 0 && lat2 <= 0 && lat3 >= linearLat) {
return true;
}
// North Pole.
if (lat3 >= PI/2) {
return true;
}
// Compare lat3 with latitude on the GC/Rhumb segment corresponding to lng3.
// Compare through a strictly-increasing function (tan() or mercator()) as convenient.
return geodesic ?
tan(lat3) >= tanLatGC(lat1, lat2, lng2, lng3) :
mercator(lat3) >= mercatorLatRhumb(lat1, lat2, lng2, lng3);
}
/**
* Returns tan(latitude-at-lng3) on the great circle (lat1, lng1) to (lat2, lng2). lng1==0.
* See http://williams.best.vwh.net/avform.htm .
*/
private static double tanLatGC(double lat1, double lat2, double lng2, double lng3) {
return (tan(lat1) * sin(lng2 - lng3) + tan(lat2) * sin(lng3)) / sin(lng2);
}
/**
* Returns mercator Y corresponding to latitude.
* See http://en.wikipedia.org/wiki/Mercator_projection .
*/
static double mercator(double lat) {
return log(tan(lat * 0.5 + PI/4));
}
/**
* Returns mercator(latitude-at-lng3) on the Rhumb line (lat1, lng1) to (lat2, lng2). lng1==0.
*/
private static double mercatorLatRhumb(double lat1, double lat2, double lng2, double lng3) {
return (mercator(lat1) * (lng2 - lng3) + mercator(lat2) * lng3) / lng2;
}
/**
*计算给定点是否位于指定多边形内。
*无论最后一个点是否等于,多边形始终为余弦闭合
*第一个或不是。
*内部被定义为不包含南极——南极总是在外部。
*如果测地线为真,则多边形由大圆段构成,并为直角形
*(loxodromic)片段。
*/
公共静态布尔包含位置(LatLng点、列表多边形、布尔测地线){
最终整数大小=polygon.size();
如果(大小==0){
返回false;
}
双纬度3=日珥半径(点纬度);
双lng3=环面(点经度);
LatLng prev=多边形.get(大小-1);
双纬度1=环面(前纬度);
双lng1=环面(上经度);
int-nIntersect=0;
用于(板条点2:多边形){
双dLng3=包裹(lng3-lng1-PI,PI);
//特殊情况:等于顶点的点在内部。
如果(lat3==lat1&&dLng3==0){
返回true;
}
双纬度2=环面(点2.纬度);
双lng2=环面(点2.经度);
//将经度偏移-lng1。
if(相交(lat1,lat2,包裹(lng2-lng1,-PI,PI),lat3,dLng3,测地线)){
++第九届;
}
/**
* Computes whether the given point lies inside the specified polygon.
* The polygon is always cosidered closed, regardless of whether the last point equals
* the first or not.
* Inside is defined as not containing the South Pole -- the South Pole is always outside.
* The polygon is formed of great circle segments if geodesic is true, and of rhumb
* (loxodromic) segments otherwise.
*/
public static boolean containsLocation(LatLng point, List<LatLng> polygon, boolean geodesic) {
final int size = polygon.size();
if (size == 0) {
return false;
}
double lat3 = toRadians(point.latitude);
double lng3 = toRadians(point.longitude);
LatLng prev = polygon.get(size - 1);
double lat1 = toRadians(prev.latitude);
double lng1 = toRadians(prev.longitude);
int nIntersect = 0;
for (LatLng point2 : polygon) {
double dLng3 = wrap(lng3 - lng1, -PI, PI);
// Special case: point equal to vertex is inside.
if (lat3 == lat1 && dLng3 == 0) {
return true;
}
double lat2 = toRadians(point2.latitude);
double lng2 = toRadians(point2.longitude);
// Offset longitudes by -lng1.
if (intersects(lat1, lat2, wrap(lng2 - lng1, -PI, PI), lat3, dLng3, geodesic)) {
++nIntersect;
}
lat1 = lat2;
lng1 = lng2;
}
return (nIntersect & 1) != 0;
}
/**
* Wraps the given value into the inclusive-exclusive interval between min and max.
* @param n The value to wrap.
* @param min The minimum.
* @param max The maximum.
*/
static double wrap(double n, double min, double max) {
return (n >= min && n < max) ? n : (mod(n - min, max - min) + min);
}
/**
* Returns the non-negative remainder of x / m.
* @param x The operand.
* @param m The modulus.
*/
static double mod(double x, double m) {
return ((x % m) + m) % m;
}
/**
* Computes whether the vertical segment (lat3, lng3) to South Pole intersects the segment
* (lat1, lng1) to (lat2, lng2).
* Longitudes are offset by -lng1; the implicit lng1 becomes 0.
*/
private static boolean intersects(double lat1, double lat2, double lng2,
double lat3, double lng3, boolean geodesic) {
// Both ends on the same side of lng3.
if ((lng3 >= 0 && lng3 >= lng2) || (lng3 < 0 && lng3 < lng2)) {
return false;
}
// Point is South Pole.
if (lat3 <= -PI/2) {
return false;
}
// Any segment end is a pole.
if (lat1 <= -PI/2 || lat2 <= -PI/2 || lat1 >= PI/2 || lat2 >= PI/2) {
return false;
}
if (lng2 <= -PI) {
return false;
}
double linearLat = (lat1 * (lng2 - lng3) + lat2 * lng3) / lng2;
// Northern hemisphere and point under lat-lng line.
if (lat1 >= 0 && lat2 >= 0 && lat3 < linearLat) {
return false;
}
// Southern hemisphere and point above lat-lng line.
if (lat1 <= 0 && lat2 <= 0 && lat3 >= linearLat) {
return true;
}
// North Pole.
if (lat3 >= PI/2) {
return true;
}
// Compare lat3 with latitude on the GC/Rhumb segment corresponding to lng3.
// Compare through a strictly-increasing function (tan() or mercator()) as convenient.
return geodesic ?
tan(lat3) >= tanLatGC(lat1, lat2, lng2, lng3) :
mercator(lat3) >= mercatorLatRhumb(lat1, lat2, lng2, lng3);
}
/**
* Returns tan(latitude-at-lng3) on the great circle (lat1, lng1) to (lat2, lng2). lng1==0.
* See http://williams.best.vwh.net/avform.htm .
*/
private static double tanLatGC(double lat1, double lat2, double lng2, double lng3) {
return (tan(lat1) * sin(lng2 - lng3) + tan(lat2) * sin(lng3)) / sin(lng2);
}
/**
* Returns mercator Y corresponding to latitude.
* See http://en.wikipedia.org/wiki/Mercator_projection .
*/
static double mercator(double lat) {
return log(tan(lat * 0.5 + PI/4));
}
/**
* Returns mercator(latitude-at-lng3) on the Rhumb line (lat1, lng1) to (lat2, lng2). lng1==0.
*/
private static double mercatorLatRhumb(double lat1, double lat2, double lng2, double lng3) {
return (mercator(lat1) * (lng2 - lng3) + mercator(lat2) * lng3) / lng2;
}
map.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() {
@Override
public void onPolygonClick(Polygon polygon) {
// Handle click ...
}
});