flutter_map icon indicating copy to clipboard operation
flutter_map copied to clipboard

Polygon onTap?

Open ibisoda opened this issue 5 years ago • 80 comments

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?

ibisoda avatar Aug 12 '19 12:08 ibisoda

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

matyhaty avatar Aug 12 '19 16:08 matyhaty

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.

neokree avatar Aug 27 '19 13:08 neokree

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;
}

neokree avatar Aug 27 '19 14:08 neokree

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.

synw avatar Aug 27 '19 15:08 synw

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.

spvalencia avatar Aug 28 '19 16:08 spvalencia

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 ?

MichalMisiaszek avatar Aug 29 '19 17:08 MichalMisiaszek

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.

MichalMisiaszek avatar Aug 29 '19 17:08 MichalMisiaszek

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

synw avatar Aug 29 '19 18:08 synw

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];
  }

MichalMisiaszek avatar Aug 29 '19 18:08 MichalMisiaszek

PSA getPolygonWithPosition does not compile, you need to convert to the classic for(var path in _polygonPaths) loop.

GregorySech avatar Aug 29 '19 18:08 GregorySech

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.

MichalMisiaszek avatar Aug 29 '19 18:08 MichalMisiaszek

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.

GregorySech avatar Aug 29 '19 18:08 GregorySech

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.

aleffabricio avatar Sep 12 '19 01:09 aleffabricio

Hi @aleffabricio. Thank you so much. I'm going to check it out.

spvalencia avatar Sep 12 '19 23:09 spvalencia

I found this useful plugin for flutter_map: https://pub.dev/packages/map_controller

joandervieira avatar Jan 28 '20 13:01 joandervieira

@joandervieira Thank you

matyhaty avatar Feb 01 '20 09:02 matyhaty

check this package geodesy it have a method to check if a given geo point is in the a polygon

X-SLAYER avatar Feb 27 '20 12:02 X-SLAYER

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,
                          ),
                        ],
                      ),
                    ),
                  ),
                );
              },
            );
          },
        ),
      ),

Anup2712 avatar Jun 11 '20 12:06 Anup2712

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),

             
            ],
          ),
        ),
      ],
    ),
  ),
);

} }`

iRELGE avatar Oct 18 '20 22:10 iRELGE

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 .

Anup2712 avatar Oct 19 '20 05:10 Anup2712

@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 avatar Feb 28 '21 17:02 kotrotko

@kotrotko your welcome ill share it soon ,ill share how to show popup if you clock on polygon if you need it

iRELGE avatar Feb 28 '21 18:02 iRELGE

@kotrotko please can you marke this issue as closed

iRELGE avatar Feb 28 '21 18:02 iRELGE

I'm so sorry status closed is not available now((

kotrotko avatar Feb 28 '21 18:02 kotrotko

@kotrotko its ok bro my its about Agriculture draw field and follow the interventions if you have any question contact me

iRELGE avatar Feb 28 '21 20:02 iRELGE

👍🏻

On 28 Feb 2021, at 19:32, RabieEL [email protected] wrote:

marke

kotrotko avatar Feb 28 '21 20:02 kotrotko

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.

github-actions[bot] avatar Mar 31 '21 02:03 github-actions[bot]

This issue was closed because it has been stalled for 5 days with no activity.

github-actions[bot] avatar Apr 05 '21 02:04 github-actions[bot]

This issue seems rather basic and would probably be very useful to many devs. I don't think it should be closed.

barbalex avatar Jun 05 '21 13:06 barbalex

also agree, thats a work around not addressing the core issue

matthiscock avatar Jun 05 '21 16:06 matthiscock