B4J Question [Solved] Build Map Polygon From Lat/Lon List

Harris

Expert
Licensed User
Longtime User
zone.PNG



Using a list of lat/lon points, how would on create a google map polygon (abmaterial) that would capture the points shown in red square programmatically?

What I am trying to accomplish:
1. Automatically create a polygon around selected points of one vehicle.
2. Use this polygon to test all other vehicles and capture their points when it has been determined they drove inside this poly (using pointinpoly function - already built).
3. Compare various params of other vehicles to base vehicle (ie. speed, braking, etc)

For simplicity, I drew this example square (4 points). I suppose it could use all 120 points and conform to the shape of this track shown...

I have been fighting with the orange example using just 4 points - the first and last point in the list - then trying to extend it to make a sqaure box.

Thanks
 

emexes

Expert
Licensed User
If that solution is good enough for your purposes then there are plenty of algorithms to solve it.
I'm still trying to work out what the end goal is. My guess is something like a mining crew in wild country needs to know where the road is nearest to them, or a similar metric is required to draw a map of cost-effectiveness to choose new mining points.

For these use-cases, simply scanning the list of GPS track points to find where the road is closest to a given point, is probably sufficient and certainly simpler and less error-prone. 👍

What is more you should be able to use Lat/Lon values directly.
Depends on your definition of a straight line on a spheroid 🤔 but I agree that for most use-cases here, they're close enough. 🍻
 
Upvote 0

TILogistic

Expert
Licensed User
Longtime User
???.
 
Upvote 0

Brian Dean

Well-Known Member
Licensed User
Longtime User
Depends on your definition of a straight line on a spheroid
Yes, @emexes, that could be a problem, but if necessary we can always do a Transverse Mercator Projection. Now I have written a B4X code class to do that, so we are almost home and dry. That's assuming that @Harris is still in the game.
 
Upvote 0

Harris

Expert
Licensed User
Longtime User
Sorry dudes, thanks for all the response. I was busy yesterday (Sunday) tending to the members of shotgun sports club. I am the resident caretaker here where I setup the target throwing machines, plow the 1.5 km long driveway, collect fees and such.

Yes, I did write the geozones tutorial to manually create a map zone. These zones are used extensively in my Android app at the mine to gather specific data when a vehicle enters a zone.

Now, using the historical GPS data collected from the vehicles, I need to create a zone "on the fly" (via code) to encompass a small section (120 points) for one particular vehicle. Here is the real reason why...

One truck drove off the road due to the driver suffering a medical incident (he blacked out - he claimed). It was clear the speed he was travelling when he left the road at the accident site - but was in unclear is what is a "normal" safe speed at this exact location. Twenty five trucks drive over this same section 8 times every day.
Using my map and graph viewer, we select a point on the speed graph which is an actual GPS point in time - and the app fetches 60 points before and after the selected point and plots them on the map.
NOW, I figure, let's create a zone (via code) around this section and use it to fetch points from all other vehicles in the fleet that travel thru it. The lat / lon buffering is required to compensate for the natural GPS wandering. Using the data from these other vehicles, we can analyze what others do at this location (safe or unsafe practices).
That is one specific example of its' use. Often, management gets reports from the driver group of unsafe practices by certain individuals at particular dangerous road sections. This new tool will allow them to quickly identify the offenders and take corrective action before another (very costly) accident needlessly occurs.

As mentioned / suggested previously, a simple method of determining the extents of the entire 120 point collection - min / max of lat/lon. Buffer this a draw a box around it for my zone. This should work and I shall now attempt a example.

Thanks to all...
 
Upvote 0

Harris

Expert
Licensed User
Longtime User
Got er licked!
Who would have thought it could have been so simple?

Granted, if this were within a city we would run into issues (possibly getting points that don't belong).
Fortunately, I have only ONE road to deal with - so large zones don't matter. Anything inside the poly (other than the 1 road) will be a grizzly bear, caribou herd, moose or flock of ptarmigan... I have seen them all on my many visits to the mine site.
I picked blueberries at 3am in mid summer up there (sun never sets)... Brought 40 pounds back with me upon return! I still bake tarts and muffins with them...

Thanks to everyone for helping my aging brain with this....

Here's the code thanks to @emexes ...

B4X:
Sub chart_chartitemclicked(Message As String)
    
    Dim sb As ABMSideBar = page.GetSideBar("SideBar")
    Dim cn As ABMContainer =  sb.Content.Component("cnt1")
    Dim us As ABMCheckbox = cn.Component("btnshowus")
    DateTime.TimeFormat = "HH:mm:ss"
    ListFindOther.Initialize
    
    Dim mark As List   ' the list of points used to draw the GeoZone (polygon) - after determining extents...
    mark.Initialize
    
    Dim latlist As List    ' the lists of points to process. These are used to extract the extents of the poly...
    Dim lonlist As List
    latlist.Initialize
    lonlist.Initialize
    
    
    Dim mes As String = Message
    
    Log(" ECM File SIze: "&ListECM.Size)
    
    Dim idx As Int = Message.IndexOf("row:")
    mes = mes.Replace("row:","")
    Dim idx As Int = mes.IndexOf(",")
    Dim val As String =""

    If idx > 0 Then
         val  = mes.SubString2(1, idx)
        Log(" rec to get: "&val)
    Else
        Log(" no index > 0")   
    End If
    
    If IsNumber(val) Then
        Dim val0 As Int = val - 60 '  make sure we have at least 60 points both for and aft of selected point...
        If val0 < 1 Then
            val0 = 1
        End If
        Dim val1 As Int = val + 60
        If val1 > ListECM.Size-1 Then
            val1 = ListECM.Size
        End If
        
        gm1.RemoveMarkers
        gm1.RemovePolygons
        
        Log(" Number of select recs: "&(val1-val0) )
        Log(" for vals - 0: "&val0&"  1: "&val1)
        
        For i = val0 To val1 -1

            Dim mpp As Map = ListECM.Get(i)
            ListFindOther.Add(mpp)  ' add all the details to a list

            Dim lt As Double = mpp.Get("501")
            Dim ln As Double = mpp.Get("502")
            
            latlist.Add(lt)   ' make 2 lists of lat / lon
            lonlist.Add(ln)
            
            
            Dim lat As String = mpp.Get("501")    ' this is used to create an info box for the google map
            Dim lon As String = mpp.Get("502")
            Dim sp As String = mpp.GetDefault("2",0)
            Dim tm As String = mpp.GetDefault("1",0)
            Dim rp As String = mpp.GetDefault("4",0)
            
            If us.State Then   ' show US miles or Metric....
                sp = NumberFormat2(sp * 0.62137 ,1,1,1,False)
            End If
            Dim dt As String = DateTime.Time(tm)
            
            Dim  legend As String = " {B} Time: "&"   "&dt&"  {BR}"
            legend = legend&" Speed: "&"   "&sp&"  {BR}"
            legend = legend&" RPM: "&"   "&rp&" {/B} {BR}"

            If i = val Then   ' show the selected point in RED...
                gm1.AddMarker( "mk"&i,lat,lon, ABM.COLOR_RED ,"Speed: "&sp&"    Time: "&"  "&dt&"  ", legend)
            Else
                gm1.AddMarker( "mk"&i,lat,lon, ABM.COLOR_Blue ,"Speed: "&sp&"    Time: "&"  "&dt&"  ",legend)
            End If
        Next
        
        gm1.SetLocation( lat,lon) '  center the map on the selected point....
        gm1.Refresh
            
    End If
    
    mark = GetCoords(latlist, lonlist)  ' call the method to get the Geozone bounds!!!!
    
    ' show (and store later) the Geozone we need...
    gm1.AddPolygon( "p1" , mark   , ABM.COLOR_DEEPORANGE, ABM.INTENSITY_NORMAL,  1.0,   5, ABM.COLOR_CYAN, ABM.INTENSITY_NORMAL,  0.3)

End Sub

Sub GetCoords(Lat As List, Lon As List) As List
    
    Dim MinLon As Double = Lon.Get(0)
    Dim MaxLon As Double = Lon.Get(0)
    Dim MinLat As Double = Lat.Get(0)
    Dim MaxLat As Double = Lat.Get(0)

    For I = 1 To Lat.Size - 1    'first point (array element 0) already done
        If Lon.Get(I) < MinLon Then
            MinLon = Lon.Get(I)
        Else
            If Lon.Get(I) > MaxLon Then    MaxLon = Lon.Get(I)
        End If

        If Lat.Get(I) < MinLat Then
            MinLat = Lat.Get(I)
        Else
            If Lat.Get(I) > MaxLat Then MaxLat = Lat.Get(I)
        End If
    Next

    'allow a bit extra in each direction to handle GPS wander etc:

'    Dim LatBuffer As Double = (50 / 40000000) * 360    '50 metres = 0.00045 degrees latitude (planet earth circumference is nominally 40,000 km = 40,000,000 m)
'    Dim LonBuffer As Double = LatBuffer / Cos(68)    '50 metres = 0.00120 degrees longitude (scale varies with latitude)
    'probably better as: LonBuffer = LatBuffer / Cos((MinLat + MaxLat) / 2) for use at other latitudes

    'bounding rectangle polygon is now the four points:

    '(MinLon - LonBuffer, MinLat - LatBuffer) to
    '(MinLon - LonBuffer, MaxLat + LatBuffer) to
    '(MaxLon + LonBuffer, MaxLat + LatBuffer) to
    '(MaxLon + LonBuffer, MinLat - LatBuffer) to
    '(MinLon - LonBuffer, MinLat - LatBuffer) starting point again, if needed to close polygon
    
    Log("MaxLat: "&MaxLat&"  MaxLon: "&MaxLon&"   MinLat: "&MaxLat&"  MinLon: "&MinLon)
        
    Dim coord As List
    coord.Initialize
    Dim buflat As Double = 0.0005  ' my buffers for simplicity....
    Dim buflon As Double = 0.0009

    coord.Addall(Array As Double(MinLat-buflat, MinLon-buflon, MaxLat+buflat, MinLon-buflon,       MaxLat+buflat, MaxLon+buflon, MinLat-buflat, MaxLon+buflon ))

    Return coord   ' done here... simple eh?

End Sub

The chart used to select a point - then get 60 more points fore and after...
Chartview.JPG


Resulting Map View...

selpoints.JPG


Another example... east to west selection....

selpoints1.JPG
 
Upvote 0

emexes

Expert
Licensed User
Got er licked!
Who would have thought it could have been so simple?
Lol, not sure you're out of the woods yet, but I like your optimism. 🍻 Do the trucks ever stop for a while, eg, to pass each other on narrow sections of the track, or a driver to water a tree? Or even "worse", do trucks back up occasionally eg if they meet head-on on narrow parts of the track?
 
Upvote 0
Top