Skip to content

Instantly share code, notes, and snippets.

@rvachev
Created October 3, 2023 16:34
Show Gist options
  • Select an option

  • Save rvachev/dd6302d50eec7a629e8cf7344a764c4b to your computer and use it in GitHub Desktop.

Select an option

Save rvachev/dd6302d50eec7a629e8cf7344a764c4b to your computer and use it in GitHub Desktop.
Handling object tap
late final MapController _mapController = MapController();
final _objects = <MapObject>[];
bool _searchForObjectsInPoints(LatLng latLng) {
// Предположительные метрики в пикселях
// 40 14.67 - 15.67
// 30 15.67 - 16.67
// 20 16.67 - 17.67
// 10 17.67 - 18.67
final precision = min((20 - _mapController.zoom) * 7, 40);
final distances = <MapObject, double>{};
for (final object in _objects) {
distances.putIfAbsent(
object,
() => _minDistanceToObject(object, latLng),
);
}
final sorted = SplayTreeMap<MapObject, double>.from(
distances,
(a, b) => distances[a]!.compareTo(distances[b]!),
);
if (sorted.isEmpty) {
return false;
}
if (sorted.values.first < precision) {
// Function which handles object tap
_onObjectTap(sorted.firstKey()!);
return true;
}
return false;
}
double _minDistanceToObject(
MapObject? object,
LatLng latLng,
) {
final tap = const Epsg3857().latLngToPoint(latLng, _mapController.zoom);
if (object is PolylineWrapper) {
return _minDistanceToPoints(object.polyline.points, tap);
}
if (object is PolygonWrapper) {
if (_isInside(object.polygon, tap)) return 0;
return _minDistanceToPoints(object.polygon.points, tap);
}
if (object is MultiPolylineWrapper) {
var minDistance = double.maxFinite;
for (final polyline in object.polylines) {
final distance = _minDistanceToPoints(polyline.points, tap);
if (distance < minDistance) {
minDistance = distance;
}
}
return minDistance;
}
if (object is MultiPolygonWrapper) {
var minDistance = double.maxFinite;
for (final polygon in object.polygons) {
if (_isInside(polygon, tap)) return 0;
final distance = _minDistanceToPoints(polygon.points, tap);
if (distance < minDistance) {
minDistance = distance;
}
}
return minDistance;
}
return double.maxFinite;
}
double _minDistanceToPoints(List<LatLng> points, CustomPoint<double> tap) {
var minDistance = double.maxFinite;
for (var i = 0; i < points.length - 1; i++) {
final firstPoint =
const Epsg3857().latLngToPoint(points[i], _mapController.zoom);
final secondPoint =
const Epsg3857().latLngToPoint(points[i + 1], _mapController.zoom);
final distanceOne =
tap.distanceTo(firstPoint) + tap.distanceTo(secondPoint);
final distanceTwo = firstPoint.distanceTo(secondPoint);
if (distanceOne - distanceTwo < minDistance) {
minDistance = distanceOne - distanceTwo;
}
}
return minDistance;
}
//https://ru.wikibooks.org/wiki/Реализации_алгоритмов/Задача_о_принадлежности_точки_многоугольнику
bool _isInside(Polygon polygon, CustomPoint<double> tap) {
final points = polygon.points;
var isInside = false;
var j = points.length - 1;
for (var i = 0; i < points.length; j = i++) {
final firstPoint =
const Epsg3857().latLngToPoint(points[i], _mapController.zoom);
final secondPoint =
const Epsg3857().latLngToPoint(points[j], _mapController.zoom);
if (((firstPoint.y < secondPoint.y) &&
(firstPoint.y <= tap.y) &&
(tap.y <= secondPoint.y) &&
((secondPoint.y - firstPoint.y) * (tap.x - firstPoint.x) >
(secondPoint.x - firstPoint.x) * (tap.y - firstPoint.y))) ||
((firstPoint.y > secondPoint.y) &&
(secondPoint.y <= tap.y) &&
(tap.y <= firstPoint.y) &&
((secondPoint.y - firstPoint.y) * (tap.x - firstPoint.x) <
(secondPoint.x - firstPoint.x) * (tap.y - firstPoint.y)))) {
isInside = !isInside;
}
}
return isInside;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment