flutter_map
flutter_map copied to clipboard
Polygon onTap?
Hi,
I'm trying to reimplement an existing Leaflet-based app using flutter. In that app, the developer used Polygons to display the outline of a building, which when clicked/tapped shows additional information. and changes the polygon.
I'm trying to replicate that function, however I noticed there is no onTap Listener, at least for Polygons. Is there a different way to listen to tap events?
We are also looking at a onTap of a polygon / live.
Would be very helpful to us
We have some developers here also, so if someone can point us in the right direction we are happy to contribute
From what I found it exist a onTap
method, settable from the MapOptions
. However this returns a LatLng
object of the position tapped, so it seems like that it is not "binded" to any Polygon, Line, Marker etc.
Just for anyone interested, I just translated in dart a function to find if a position is inside a Polygon
/// Translated from PHP
/// Source: https://assemblysys.com/php-point-in-polygon-algorithm/
bool _pointInPolygon(LatLng position, Polygon polygon) {
// Check if the point sits exactly on a vertex
var vertexPosition = polygon.points
.firstWhere((point) => point == position, orElse: () => null);
if (vertexPosition != null) {
return true;
}
// Check if the point is inside the polygon or on the boundary
int intersections = 0;
var verticesCount = polygon.points.length;
for (int i = 1; i < verticesCount; i++) {
LatLng vertex1 = polygon.points[i - 1];
LatLng vertex2 = polygon.points[i];
// Check if point is on an horizontal polygon boundary
if (vertex1.latitude == vertex2.latitude &&
vertex1.latitude == position.latitude &&
position.longitude > min(vertex1.longitude, vertex2.longitude) &&
position.longitude < max(vertex1.longitude, vertex2.longitude)) {
return true;
}
if (position.latitude > min(vertex1.latitude, vertex2.latitude) &&
position.latitude <= max(vertex1.latitude, vertex2.latitude) &&
position.longitude <= max(vertex1.longitude, vertex2.longitude) &&
vertex1.latitude != vertex2.latitude) {
var xinters = (position.latitude - vertex1.latitude) *
(vertex2.longitude - vertex1.longitude) /
(vertex2.latitude - vertex1.latitude) +
vertex1.longitude;
if (xinters == position.longitude) {
// Check if point is on the polygon boundary (other than horizontal)
return true;
}
if (vertex1.longitude == vertex2.longitude ||
position.longitude <= xinters) {
intersections++;
}
}
}
// If the number of edges we passed through is odd, then it's in the polygon.
return intersections % 2 != 0;
}
This lib can do it as well. The problem is that this kind of brute force geofencing is really slow so not user friendly. If you have a lot of polygons it is not appropriate. It would be much better to have an on tap callback.
It would be great to have onTap event for polygons and polylines. I also need that feature but I have not be able to find a good way to implemented.
Interestingly enough the same authors created map_view plugin which uses static Google Maps and provide listeners for polygon layers. @johnpryan how difficult it to add to flutter_map ?
Just for anyone interested, I just translated in dart a function to find if a position is inside a Polygon
/// Translated from PHP /// Source: https://assemblysys.com/php-point-in-polygon-algorithm/ bool _pointInPolygon(LatLng position, Polygon polygon) { // Check if the point sits exactly on a vertex var vertexPosition = polygon.points .firstWhere((point) => point == position, orElse: () => null); if (vertexPosition != null) { return true; } // Check if the point is inside the polygon or on the boundary int intersections = 0; var verticesCount = polygon.points.length; for (int i = 1; i < verticesCount; i++) { LatLng vertex1 = polygon.points[i - 1]; LatLng vertex2 = polygon.points[i]; // Check if point is on an horizontal polygon boundary if (vertex1.latitude == vertex2.latitude && vertex1.latitude == position.latitude && position.longitude > min(vertex1.longitude, vertex2.longitude) && position.longitude < max(vertex1.longitude, vertex2.longitude)) { return true; } if (position.latitude > min(vertex1.latitude, vertex2.latitude) && position.latitude <= max(vertex1.latitude, vertex2.latitude) && position.longitude <= max(vertex1.longitude, vertex2.longitude) && vertex1.latitude != vertex2.latitude) { var xinters = (position.latitude - vertex1.latitude) * (vertex2.longitude - vertex1.longitude) / (vertex2.latitude - vertex1.latitude) + vertex1.longitude; if (xinters == position.longitude) { // Check if point is on the polygon boundary (other than horizontal) return true; } if (vertex1.longitude == vertex2.longitude || position.longitude <= xinters) { intersections++; } } } // If the number of edges we passed through is odd, then it's in the polygon. return intersections % 2 != 0; }
I used diffrent trick. Each polygon in lat/lng space I convert into a path in x/y space. Then using 2d geometry functions of path I can check if given point is inside specific path (which I can connect to specific polygon). Works pretty fast.
Interesting. Do you have some code that we could see? I'm trying to do more or less the same thing using the geohex encoding format
Interesting. Do you have some code that we could see? I'm trying to do more or less the same thing using the geohex encoding format
So I am using some classes from actual map plugin to convert points from lat/lng to x/y. I iterate over all polygons and store paths in List in the same order.
import 'dart:ui' as ui;
ui.Path convertPolygon(Polygon polygon) {
CustomPoint _customPoint;
List<Offset> _offsets = new List<Offset>();
ui.Path _polygonPath = new ui.Path();
polygon.points.forEach((point) {
_customPoint = _mapOptions.crs.latLngToPoint(point, _mapOptions.zoom);
_offsets.add(new Offset(_customPoint.x, _customPoint.y));
});
_polygonPath.addPolygon(_offsets, true);
return _polygonPath;
}
and then:
Polygon getPolygonWithPosition(LatLng _position) {
CustomPoint _customPoint = _mapOptions.crs.latLngToPoint(_position, _mapOptions.zoom);
Offset _offset = new Offset(_customPoint.x, _customPoint.y);
int _index = -1;
_polygonPaths.forEach((path) {
_index ++;
if (path.contains(_offset)) {
return;
}
});
return _polygons[index];
}
PSA getPolygonWithPosition does not compile, you need to convert to the classic for(var path in _polygonPaths) loop.
PSA getPolygonWithPosition does not compile, you need to convert to the classic for(var path in _polygonPaths) loop.
I just changed code in answer from original. Let me fix it. Fixed.
I do not enjoy being pedantic but using return does not stop functional forEach, the intent is clear tho.
EDIT:
int _index = _polygonPaths.indexWhere((path) => path.contains(_offset));
Could be an alternative to forEach.
It would be great to have onTap event for polygons and polylines. I also need that feature but I have not be able to find a good way to implemented.
Hello, @spvalencia, this library has been implemented https://github.com/synw/geodraw, sometimes it helps you in some way.
Hi @aleffabricio. Thank you so much. I'm going to check it out.
I found this useful plugin for flutter_map: https://pub.dev/packages/map_controller
@joandervieira Thank you
check this package geodesy it have a method to check if a given geo point is in the a polygon
I have done ontap of polygon and show info window using map controller
FlutterMap(
mapController: mapController,
options: MapOptions(
center: LatLng(20.1754, 84.4053),
zoom: 8,
onTap: (latlng) {
showDialog(
context: context,
builder: (context) {
return Dialog(
child: Container(
height: MediaQuery.of(context).size.height * 0.3,
width: MediaQuery.of(context).size.width * 0.2,
decoration: BoxDecoration(
border: Border.all(
color: Colors.blue,
width: 8,
),
),
child: Align(
alignment: Alignment.topLeft,
child: Column(
children: <Widget>[
Text(
"Latitude : " + plotlat + "",
style: _textStyleinfo,
),
Divider(
color: Colors.black54,
),
Text(
"Longitude : " + plotlon + "",
style: _textStyleinfo,
),
],
),
),
),
);
},
);
},
),
),
you can use my code i used to know if point is inside polygon i hope it help you. soon i'll share i code that show you a pop up on map if point is inside polygon: inside pubspec.yaml: flutter_map and geodesy.
`class PolylinePage extends StatelessWidget { static const String route = 'polyline'; Geodesy geodesy = Geodesy(); @override Widget build(BuildContext context) { // List of point to draw polygon var points = <LatLng>[ LatLng(51.5, -0.09), LatLng(53.3498, -6.2603), LatLng(48.8566, 2.3522), ]; //List of point to draw second polygon var pointsGradient = <LatLng>[ LatLng(55.5, -0.09), LatLng(54.3498, -6.2603), LatLng(52.8566, 2.3522), LatLng(55.5, -0.09), LatLng(55.5, -0.09), ];
var polygones = <Polygon>[
Polygon(points: points, color: Colors.red),
Polygon(points: pointsGradient)
];
void onPolygon(LatLng point) {
polygones.forEach((element) {
// if point is on the polygon isGeoPointInPolygon iS true
bool isGeoPointInPolygon =
geodesy.isGeoPointInPolygon(point, element.points);
if (isGeoPointInPolygon == true) {
print(element.points);
}
});
}
return Scaffold(
appBar: AppBar(title: Text('Polylines')),
drawer: buildDrawer(context, PolylinePage.route),
body: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(top: 8.0, bottom: 8.0),
child: Text('Polylines'),
),
Flexible(
child: FlutterMap(
options: MapOptions(
///use on tap to get point coordinates
onTap: onPolygon,
plugins: [
/// import plugin
TappablePolylineMapPlugin(),
],
center: LatLng(51.5, -0.09),
zoom: 5.0,
),
layers: [
TileLayerOptions(
urlTemplate:
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
subdomains: ['a', 'b', 'c']),
// to draw polygon on map
PolygonLayerOptions(polygons: polygones),
],
),
),
],
),
),
);
} }`
thank you
With Regards,Anupam DasMobile Application DeveloperSPARC Pvt. Ltd.
On Mon, Oct 19, 2020 at 3:48 AM RabieEL [email protected] wrote:
you can use my code i used to know if point is inside polygon i hope it help you. soon i'll share i code that show you a pop up on map if point is inside polygon: inside pubspec.yaml: flutter_map and geodesy `class PolylinePage extends StatelessWidget { static const String route = 'polyline'; Geodesy geodesy = Geodesy(); @override https://github.com/override Widget build(BuildContext context) { // List of point to draw polygon var points = [ LatLng(51.5, -0.09), LatLng(53.3498, -6.2603), LatLng(48.8566, 2.3522), ]; //List of point to draw second polygon var pointsGradient = [ LatLng(55.5, -0.09), LatLng(54.3498, -6.2603), LatLng(52.8566, 2.3522), LatLng(55.5, -0.09), LatLng(55.5, -0.09), ];
var polygones = <Polygon>[ Polygon(points: points, color: Colors.red), Polygon(points: pointsGradient) ];
void onPolygon(LatLng point) {
polygones.forEach((element) {
// if point is on the polygon isGeoPointInPolygon if true bool isGeoPointInPolygon = geodesy.isGeoPointInPolygon(point, element.points); if (isGeoPointInPolygon == true) { print(element.points); } }); }
return Scaffold( appBar: AppBar(title: Text('Polylines')), drawer: buildDrawer(context, PolylinePage.route), body: Padding( padding: EdgeInsets.all(8.0), child: Column( children: [ Padding( padding: EdgeInsets.only(top: 8.0, bottom: 8.0), child: Text('Polylines'), ), Flexible( child: FlutterMap( options: MapOptions( ///use on tap to get point coordinates onTap: onPolygon, plugins: [ /// import plugin TappablePolylineMapPlugin(), ], center: LatLng(51.5, -0.09), zoom: 5.0, ), layers: [ TileLayerOptions( urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png http://tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By%7D.png', subdomains: ['a', 'b', 'c']), // to draw polygon on map PolygonLayerOptions(polygons: polygones),
], ), ), ], ),
), );
} }`
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/fleaflet/flutter_map/issues/385#issuecomment-711431985, or unsubscribe https://github.com/notifications/unsubscribe-auth/AJ2HXFGBY5QMPBWTBI5SWGDSLNSUBANCNFSM4ILA5RIQ .
@iRELGE Thank you so much for this! You saved me a lot of time and energy. If you have more publications on this issue, please, share them with me.
@kotrotko your welcome ill share it soon ,ill share how to show popup if you clock on polygon if you need it
@kotrotko please can you marke this issue as closed
I'm so sorry status closed is not available now((
@kotrotko its ok bro my its about Agriculture draw field and follow the interventions if you have any question contact me
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.
This issue was closed because it has been stalled for 5 days with no activity.
This issue seems rather basic and would probably be very useful to many devs. I don't think it should be closed.
also agree, thats a work around not addressing the core issue