Check if GPS is within a polygonal area

Hello forum,

I have a question regarding programming in arduino.

I have a gps unit and got it to store long and lat.

Now I want to write a piece of code that check whether you are in a specific area and than does something with that.

the area is something like this :


__|

So if gps is in between these four points do....

I hope that someone can help me out!

thanks!

Rens

Helo and welcome :slight_smile:

See this website for a starting point : Determining Whether A Point Is Inside A Complex Polygon

You may find other, ready-to-use functions if you search a little more :slight_smile:

Hm I tried looking for an example but I can't seem to find one.

I do not completely understand how this thing is working, I mean I understand the principle but not how I can use it in my case. For me they are for example not very clear about where I need to fill in the point it checks and where the points are filled in that make up the polygon.

As a first stab, could you do a Sutherland-Cohen style clipping operation, to trivially exclude points outside the minimum bounding box, then start to get clever with triangles?
I guess one question you have to ask is whether or not you can treat the area as a plane, or if spherical geometry needs to be considered.

From the link from guix, consider a polygon that has co-ordinates like below ( 0,0 top left, clockwise vertices. )


__|

0,0
5,0
5,5
2,5

You would setup that function like:

//  int    polySides  =  how many corners the polygon has
//  float  polyX[]    =  horizontal coordinates of corners
//  float  polyY[]    =  vertical coordinates of corners
//  float  x, y       =  point to be tested

int    polySides = 4;
float  polyX[] = { 0, 5, 5, 2 };
float  polyY[] = { 0, 0, 5, 5 };
float  x, y; //What ever point GPS provides.

Here is a 2D geometry document I found useful: Efficient 2D Geometric Operations

I made this simple, working example:

http://codepad.org/3EwobfpQ

In this example, the polygon looks like:

Y

^
|
|     A               B
|      /------------| 
|     /             |
|    /______________|
|  D                  C
+--------------------------------------> X

     X  ;  Y
A { 3.0 ; 4.0 }
B { 7.0 ; 4.0 }
C { 7.0 ; 2.0 }
D { 2.0 ; 2.0 }

guix:
I made this simple, working example:

C++ code - 33 lines - codepad

In this example, the polygon looks like:

Y

^
|
|     A               B
|      /------------|
|     /             |
|    /______________|
|  D                  C
+--------------------------------------> X

X  ;  Y
A { 3.0 ; 4.0 }
B { 7.0 ; 4.0 }
C { 7.0 ; 2.0 }
D { 2.0 ; 2.0 }

Awesome this is something I can work with I think :slight_smile:

I am sorry I'm not very experienced with complex code and taking C++ to arduino I found this:

http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

But had no idea how to apply it.

I see that you use printf and bool? I guess this is Serial.print command and a boolean? function?

Thanks for the effort!

I see that you use printf and bool? I guess this is Serial.print command and a boolean? function?

Yes replace printf by Serial.print's if you need to. And "bool" or "boolean", same thing :slight_smile:

ok cool i think i made it work :slight_smile:

const uint8_t polySides = 4; // 4 sides in your polygon

//                          A,   B,   C,   D
float polyX[polySides] = { 3.0, 7.0, 7.0, 2.0 }; // X
float polyY[polySides] = { 4.0, 4.0, 2.0, 2.0 }; // Y

void setup(){
  Serial.begin(9600);
}

void loop()
{
 
   Serial.println( pointInPolygon( 3.0, 2.5 )); // should be true
    Serial.println(pointInPolygon( 1.5, 3.0 )); // should be false
    Serial.println(pointInPolygon( 5.0, 4.5 )); // should be false
    Serial.println(pointInPolygon( 6.0, 3.5 ));  // should be true

}

bool pointInPolygon( float x, float y )
{
  int i, j = polySides - 1;
  bool oddNodes = false;

  for ( i = 0; i < polySides; i++ )
  {
    if ( (polyY[i] < y && polyY[j] >= y || polyY[j] < y && polyY[i] >= y) &&  (polyX[i] <= x || polyX[j] <= x) )
    {
      oddNodes ^= ( polyX[i] + (y - polyY[i]) / (polyY[j] - polyY[i]) * (polyX[j] - polyX[i]) < x );
    }

    j = i;
  }

  return oddNodes;
}

Good :slight_smile:

AWOL:
See those italics?
That's why we ask you to post code between code tags.

sorry I fixed it XD

guix:
Good :slight_smile:

Cool I checked with gps coordinates and it works like a charm.

Shall I make a reference to you(the maker) or the original code in my code somewhere?

I'm very relieved that guix responded to lcj's "programming in arduino" question with a link to a site which presents a technique and not just a pile of code.

All too often over the last 30+ years I've seen programmers diving into code without thinking about what it is they're actually trying to do.

lcj's post is not a programming question (less yet a programming in Arduino question)- it's a topology question, one solution of which which happens to be implemented with some code.

Karma++ to guix.....

lcj_alkemade:
Shall I make a reference to you(the maker) or the original code in my code somewhere?

Absolutely not to me, I didn't make this function and I don't know who did (it's probably older than the first computer) :slight_smile:

JimboZA:
Karma++ to guix.....

Thanks :wink:

guix:
I made this simple, working example:

C++ code - 33 lines - codepad

In this example, the polygon looks like:

Y

^
|
|     A               B
|      /------------|
|     /             |
|    /______________|
|  D                  C
+--------------------------------------> X

X  ;  Y
A { 3.0 ; 4.0 }
B { 7.0 ; 4.0 }
C { 7.0 ; 2.0 }
D { 2.0 ; 2.0 }

Thanks a lot for this wonderfull pice of code. It's working as expected with a gps receiver on arduino. I just have to figure it out how make a notification when entered in the polygon and when exit.

Necrothread.

BTW, the code fails when segment AD is nearly vertical. There's got to be a better behaved snippet somewhere... This problem is called the Point in Polygon test. Links from that wiki page lead to a C++ implementation with the same problem. :stuck_out_tongue:

The problematic code:

oddNodes ^= ( polyX[i] + (y - polyY[i]) / (polyY[j] - polyY[i]) ...
                                          ^ divide by zero!

However, this is actually a Line Segment Intersection test. Here is recent thread that used that test for a Lap Timer, and a stackoverflow answer (Gareth Rees' answer) with some code that does not depend on the orientation being non-vertical.

Here is a snippet of Shift314's implementation for the Arduino:

    s1_x = p1_x - p0_x;     
    s1_y = p1_y - p0_y;
    s2_x = fix.longitudeL() - lastFix.longitudeL();     
    s2_y = fix.latitudeL() - lastFix.latitudeL();
    s = (-s1_y * (p0_x - lastFix.longitudeL()) + s1_x * (p0_y - lastFix.latitudeL())) / (-s2_x * s1_y + s1_x * s2_y);
    t = ( s2_x * (p0_y - lastFix.latitudeL()) - s2_y * (p0_x - lastFix.longitudeL())) / (-s2_x * s1_y + s1_x * s2_y);
    
    if (s >= 0 && s <= 1 && t >= 0 && t <= 1)  {
        // Intersected!

As is clearly obvious to the most casual observer, it is a small matter of programming to replace the problematic code with the correct line segment test, and is best left as an exercise for the reader.

Tongue Firmly in Cheek,
/dev

Hello! I had test with GPS and arduino to check incoming GPS coordinates are within polygon and show estimated speed limit if GPS is within polygon. But the result had shown estimated speed limit every time. What's wrong? Please, correct me. :)
Here's the code:
[code/]#include<TinyGPS++.h>
#include<SoftwareSerial.h>
const uint8_t polySides = 4; // 4 sides in your polygon
//                             A,   B,   C,   D
float polyX[polySides] = { 16.875162,16.875465,16.875239,16.874911}; // X
float polyY[polySides] = { 96.118422,96.118304,96.117724,96.117832}; // Y

#define Defaultspeedlimit 50
float Z;
float speedlimit=20;
TinyGPSPlus gps;
SoftwareSerial ss(10, 11);

void setup(){
  Serial.begin(9600);
  ss.begin(9600);
}

void loop()
{  
 float X,Y,S;
 bool Insidespeedzone;
 
 printDateTime(gps.date, gps.time);
 
 Serial.print("Lat: ");
  X=printFloat(gps.location.lat(),gps.location.isValid(),11,6);
  
 Serial.print("Long: ");
  Y=printFloat(gps.location.lng(), gps.location.isValid(), 12, 6);
  
 Serial.print("Actual speed(kmph): ");
  S=printFloat(gps.speed.kmph(), gps.speed.isValid(), 6, 2);
  
  Insidespeedzone=pointInPolygon(X,Y,S);
Serial.print(Insidespeedzone);

if(Insidespeedzone=true)
  {Serial.print("  Estimated Speedlimit in curve: ");
 Serial.print(speedlimit);} 
 Serial.println();
 
   smartDelay(1000);
   if (millis() > 5000 && gps.charsProcessed() < 10)
   Serial.println(F("No GPS data received: check wiring"));
}

static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}

bool pointInPolygon( float x,float y,float s)
{
  int i, j = polySides - 1;
  bool inside=false;
 
    for (i = 0; i < polySides; j = i++) {
    if ( ((polyX[i]> x) != (polyX[j] > x)) &&
         (y < (polyY[j] - polyY[i]) * (x - polyX[i]) / (polyX[j] - polyX[i]) + polyY[i]) )
       inside = !inside;
       }
  
if(s>speedlimit)
  {Serial.print("Vehicle is overpseed");
  Serial.print("Estimated Speedlimit(kmph): ");
  Serial.println(speedlimit);}
  return inside;
  
}

float printFloat(float val, bool valid, int len, int prec)
{
  if (!valid)
  {
    while (len-- > 1)
    Serial.print('*');
    Serial.print(' ');
  }
  else
  {
    Serial.print(val, prec);
    Z=val,prec;
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1); // . and -
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
    Serial.print(' ');
    
  }
 smartDelay(0);
 return Z ;
 
}

static void printDateTime(TinyGPSDate &d, TinyGPSTime &t)
{
  if (!d.isValid())
  {
    Serial.print(F("********** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year());
    Serial.print(sz);
  }
  
  if (!t.isValid())
  {
    Serial.print(F("******** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second());
    Serial.print(sz);
  }

  
  smartDelay(0);
}
void loop()
{ 
 float X,Y,S;
 bool Insidespeedzone;
 
 printDateTime(gps.date, gps.time);

Printing the date and time from the gps instance, without having read anything from the GPS does not make sense.

if(Insidespeedzone=true)

= is the assignment operator. You just set Insidespeedzone to true. That is not what you wanted to do, is it?

Your camel needs a long drink of water. Its humps went flat.

:0 Oh! I didn't know my fault. I was wrong.
I've correct it.

Thank you very much, PaulS. :slight_smile:

I tried to make one for myself and it worked perfectly, now the problem I encountered is when I want two polygons and test whether the point is in polygon 1 or polygon two...
what alteration/addition in the code should i make?

Thank you guys