我正在使用 google-maps-flutter 插件开发 flutter 项目,我想检查用户位置是否位于我在地图上创建的多边形内。有一种使用 JavaScript api(containsLocation() 方法)的简单方法,但对于 flutter,我只找到了一个第三方插件,google_map_polyutil,它仅适用于 android,当我运行我的应用程序时,我遇到了安全蠕虫。还有其他方法吗?
我找到了this答案,只是修改了一些小东西以与 dart 一起使用,我对硬编码的多边形进行了测试。列表 _area 是我的多边形,而 _polygons 是我的地图控制器所必需的。
final Set<Polygon> _polygons = {};
List<LatLng> _area = [
LatLng(-17.770992200, -63.207739700),
LatLng(-17.776386600, -63.213576200),
LatLng(-17.778348200, -63.213576200),
LatLng(-17.786848100, -63.214262900),
LatLng(-17.798289700, -63.211001300),
LatLng(-17.810547700, -63.200701600),
LatLng(-17.815450600, -63.185252100),
LatLng(-17.816267800, -63.170660900),
LatLng(-17.800741300, -63.153838100),
LatLng(-17.785867400, -63.150919800),
LatLng(-17.770501800, -63.152636400),
LatLng(-17.759712400, -63.160361200),
LatLng(-17.755952300, -63.169802600),
LatLng(-17.752519100, -63.186625400),
LatLng(-17.758404500, -63.195551800),
LatLng(-17.770992200, -63.206538100),
LatLng(-17.770996000, -63.207762500)];
函数这样结束:
bool _checkIfValidMarker(LatLng tap, List<LatLng> vertices) {
int intersectCount = 0;
for (int j = 0; j < vertices.length - 1; j++) {
if (rayCastIntersect(tap, vertices[j], vertices[j + 1])) {
intersectCount++;
}
}
return ((intersectCount % 2) == 1); // odd = inside, even = outside;
}
bool 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 > 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;
}
注意 Polygons 属性和 onTap 方法。我试图检查在地图中创建的标记是否在我的多边形内:
GoogleMap(
initialCameraPosition: CameraPosition(
target: target, //LatLng(0, 0),
zoom: 16,
),
zoomGesturesEnabled: true,
markers: markers,
polygons: _polygons,
onMapCreated: (controller) =>
_mapController = controller,
onTap: (latLng) {
_getAddress(latLng);
},
)
然后我在 _getAddress 方法中使用了以下调用:
_checkIfValidMarker(latLng, _area);
我希望它能帮助你创造你需要的东西。
最简单的使用方法 - https://pub.dev/packages/maps_toolkit
with PolygonUtil.containsLocation - 计算给定点是否位于指定多边形内。
L。 Chi的回答确实有帮助。 但由于我的点非常接近,如果
rayCastIntersect
等于 aX
,则
bX
可能会得到错误的布尔返回
因此,我只需在计算
aX == bX
之前添加 m
条件检查即可。
bool 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 > 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
}
if (aX == bX) {
return true;
}
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;
}
我偶然发现了这篇文章,因为我想解决我的代码中的类似问题。不幸的是,这两个答案都错过了一个边缘情况,以及 Poly 库中的颤振点。
如果 on 边是垂直边,则 m 将为无穷大。还可以通过检查该点是否位于边缘两个点的西侧(或“左侧”)来避免这种情况,因为该条件对于垂直边缘始终成立。 为了充分理解这个问题,我推荐这个视频。
改进代码:
class Point {
Point({required this.x, required this.y});
/// X axis coordinate or longitude
double x;
/// Y axis coordinate or latitude
double y;
}
class Poly {
/// Check if a Point [point] is inside a polygon representing by a List of Point [vertices]
/// by using a Ray-Casting algorithm
static bool isPointInPolygon(Point point, List<Point> vertices) {
int intersectCount = 0;
for (int i = 0; i < vertices.length; i += 1) {
if (Poly.rayCastIntersect(point, vertices[i], vertices[(i + 1)%vertices.length])) {
intersectCount += 1;
}
}
if( intersectCount != 0){
print(intersectCount);
}
return (intersectCount % 2) == 1;
}
/// Ray-Casting algorithm implementation
/// Calculate whether a horizontal ray cast eastward from [point]
/// will intersect with the line between [vertA] and [vertB]
/// Refer to `https://en.wikipedia.org/wiki/Point_in_polygon` for more explanation
/// or the example comment bloc at the end of this file
static bool rayCastIntersect(Point point, Point vertA, Point vertB) {
final double aY = vertA.y;
final double bY = vertB.y;
final double aX = vertA.x;
final double bX = vertB.x;
final double pY = point.y;
final double pX = point.x;
if ((aY > pY && bY > pY) || (aY < pY && bY < pY) || (aX < pX && bX < pX)) {
// The case where the ray does not possibly pass through the polygon edge,
// because both points A and B are above/below the line,
// or both are to the left/west of the starting point
// (as the line travels eastward into the polygon).
// Therefore we should not perform the check and simply return false.
// If we did not have this check we would get false positives.
return false;
}
if (pX < aX && pX < bX) {
// The case where the point is to the left(west) of both points A and B,
// So we dont need to check, because the ray will certanly intersect with the polygon edge.
return true;
}
// y = mx + b : Standard linear equation
// (y-b)/m = x : Formula to solve for x
// M is rise over run -> the slope or angle between vertices A and B.
double m = (aY - bY) / (aX - bX);
// B is the Y-intercept of the line between vertices A and B
final double b = ((aX * -1) * m) + aY;
// We want to find the X location at which a flat horizontal ray at Y height
// of pY would intersect with the line between A and B.
// So we use our rearranged Y = MX+B, but we use pY as our Y value
final double x = (pY - b) / m;
// If the value of X
// (the x point at which the ray intersects the line created by points A and B)
// is "ahead" of the point's X value, then the ray can be said to intersect with the polygon.
return x > pX;
}
}