Closed ibisoda closed 9 months ago
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 =
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 notifications@github.com 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(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
👍🏻
On 28 Feb 2021, at 19:32, RabieEL notifications@github.com wrote:
marke
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
I fully agree with @barbalex, this issue seems rather basic and having to scroll through a list of polygons and verify that a point is in is brute force and greatly limits usage. Unfortunately, we didn't find simple things like putting a label in the center of a polygon in a simple way, which is quite used in any gis app.
really hope this feature can be implemented in flutter_map, most other map libraries have this feature
Doesn't the solution listed earlier work for you ?
A Polygon is basically a CustomPainter widget right? Can't we expose a onTap function inside the Polygon class and internally add a GestureDetector to the render implementation
@aytunch Might try to do some general cleanup tomorrow, so might investigate this then as well. Just got a lot on my plate at the moment!
Thanks @JaffaKetchup for the quick response. Going through lots of polygons and using geodesy's isInPolygon method makes it very slow to decide which polygon is pressed. This would be a nice improvement :D
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.
Sorry I haven't been active on this thread for a while. I never got round to looking what we could do properly. I'll try and do it today (towards the end), now things have calmed down a bit.
Ok, so I've been looking at this file:
https://github.dev/fleaflet/flutter_map/blob/aec827495aa5a5278fbd88b09b0e1d5b67dee7fa/lib/src/layer/polygon_layer.dart#L63
... and it appears that it should just be possible to wrap with a GestureDetector()
. However, the tricky bit will be send the event up the tree, as I've not had experience with this before. @ibrierley or @aytunch do you have any ideas?
I'm not about much until Monday now to test anything, but taking a quick peek at the code, I think all the Gesture stuff should probably exist in https://github.com/fleaflet/flutter_map/tree/master/lib/src/gestures
What I'm wondering though, is if thinking about gestures is a redherring...I don't think (but maybe wrong these days!) we can intercept a gesture on a shape or whatever, it would probably only be the whole canvas anyway ? So what does it get us (I may be thinking about this wrong though!) Wouldn't we still just have a point that we need to figure out if its contained in some poly ?
If that's the case, then we already have an onTap (or whatever) mapEvent set via the onTap param, or I guess listen via a mapController, and the main issue is efficiently figuring if the event is contained within an area ? (I do have some thoughts on that, but may need to be part of a bigger solution to be really efficient, how many polys may there be...).
Note, I may not be making sense :D.
Hi @JaffaKetchup thanks for looking in to this.
At a quick glance I think we can create optional uniqueId
and onTap
fields to the Polygon
class
final String uniqueId;
final Function(String) onTap;
and in line 114 we can wrap each Polygon widget with a GestureDetector:
polygons.add(
GestureDetector(
onTap: () {
if(polygon.onTap != null)
polygon.onTap(polygon.id);
},
CustomPaint(
painter: PolygonPainter(polygon),
size: size,
),
),
);
We can add other onTap
methods of GestureDetector
like onLongPress
etc to the API as well.
One concern I have is if CustomPaint
plays well with gesture detectors. I have a feeling that it might receive all the taps that fall in to its bounding box. In that case there are codes which tell if the touch point falls inside of a Polygon.
@ibrierley I think if we use the whole map's onTap
then we will need to check if users tap location corresponds to any of our maybe thousands of Polygons.
If we lower this logic to the Polygon layer, then it would be much performant I think.
And the top level Api would look like this (PolygonLayerOptions
instead of PolylineLayerOptions
)
PolylineLayerOptions(
polylines: [
Polyline(
id: "polygon1",
onTap: (id) => print("polygon with id: $id is pressed!"),
points: pointsGradient,
strokeWidth: 4.0,
gradientColors: [
Color(0xffE40203),
Color(0xffFEED00),
Color(0xff007E2D),
],
),
],
),
Seems like a good idea. Can't make a PR right now, but feel free to if you want!
I'm probably misunderstanding how the gesturedetector works a bit. But I'm assuming if you have 100 polygons in this case, you would end up with 100 overlaid gesturedetectors on each whole canvas which will all register an onTap ?
Yes, you are right. I'm not sure what that will do to performance, but I guess we'll have to find out.
@ibrierley yes exactly. Do you think this would be a problem in terms of performance? Unless a Polygon region is pressed, the onTap
will not run.
The heavy calculation part is implementing the algorithm which finds if a point is inside of a polygon.
And instead of iterating through 100 polygons to see which one(s) are pressed, We will only check if the onTap
location is inside of that specific polygon.
There are cases where 2 polygons might be overlapping. In this case I don't know how GestureDetector
s can return both of those polygons. This is a valid concern.
But in terms of performance I don't think wrapping polygons with GestureDetector
s would have an impact.
Ah I think the bit I'm misunderstanding is the "Unless a Polygon region is pressed, the onTap will not run", do we know this is the case even for one poly that it would only register a tap on the shape and not on the space next to it on the same Canvas ?
Ah I think the bit I'm misunderstanding is the "Unless a Polygon region is pressed, the onTap will not run", do we know this is the case even for one poly that it would only register a tap on the shape and not on the space next to it on the same Canvas ?
@ibrierley I need to look at CustomPainter
class and see if it allows us to register a tap according to its exact shape not the bounding box.
Did I understand your concern right? You think that a Polygon's GestureDetector
might take more area than the actual Polygon and this would mess up other gestures of the map?
Yes, my understanding (more from how Canvas worked vs svg in a browser) is that the Canvas doesn't know anything about the objects displayed, so it can't do that. However, Flutter does do some funky things with it's rendering, so it's quite possibly I'm wrong on this and it either does it, or there's a way around that.
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?