If the passed tLL list is sorted asc on Lng and asc on Lat (in this order) and have ConvexHull like this:Bit of improvement by changing this:
B4X:'Find the leftmost point For i = 1 To iPoints - 1
To this:
B4X:'Find the leftmost point For i = 0 To iPoints - 1
Not sure why it was 1 in the C++ code.
RBS
Sub ConvexHull(lst_tLL As List, iPoints As Int) As List
Dim i As Int
Dim iLeft As Int
Dim tLL As TMapLatLng
Dim tLL_Left As TMapLatLng
Dim iP As Int
Dim iQ As Int
Dim lst_tLLBorder As List
'There must be at least 3 points
If iPoints < 3 Then Return lst_tLLBorder 'return un-initialized list
lst_tLLBorder.Initialize
' 'Find the leftmost point
' For i = 0 To iPoints - 1
' tLL = lst_tLL.Get(i)
' tLL_Left = lst_tLL.Get(iLeft)
' If tLL.fLng < tLL_Left.fLng Then
' iLeft = i
' End If
' Next
' Start from leftmost point, keep moving counterclockwise
' until reach the start point again.
Log("iP: " & iP & ", iLeft: " & iLeft)
Do While True 'iP <> iLeft
'Add current point to result
lst_tLLBorder.Add(lst_tLL.Get(iP))
' Search for a point 'q' such that orientation(p, q, x) is counterclockwise for all points 'x'.
iQ = (iP + 1) Mod iPoints
For i = 0 To iPoints - 1
'If i is more counterclockwise than current q, then update q
'If Orientation(points(p), points(i), points(q)) = 2 Then
If Orientation(lst_tLL.Get(iP), lst_tLL.Get(i), lst_tLL.Get(iQ)) = 2 Then
iQ = i
End If
Next
' Now q is the most counterclockwise with respect to p
' Set p as q for next iteration, so that q is added to result 'hull'
iP = iQ
Log("iP: " & iP)
If iP = iLeft Then
Exit
End If
Loop
Return lst_tLLBorder
' Print Result
' For i As Integer = 0 To hull.Count - 1
' Console.WriteLine("(" & hull(i).x & ", " & hull(i).y & ")")
' Next
End Sub
The sort on Lat is actually not needed.If the passed tLL list is sorted asc on Lng and asc on Lat (in this order) and have ConvexHull like this:
B4X:Sub ConvexHull(lst_tLL As List, iPoints As Int) As List Dim i As Int Dim iLeft As Int Dim tLL As TMapLatLng Dim tLL_Left As TMapLatLng Dim iP As Int Dim iQ As Int Dim lst_tLLBorder As List 'There must be at least 3 points If iPoints < 3 Then Return lst_tLLBorder 'return un-initialized list lst_tLLBorder.Initialize ' 'Find the leftmost point ' For i = 0 To iPoints - 1 ' tLL = lst_tLL.Get(i) ' tLL_Left = lst_tLL.Get(iLeft) ' If tLL.fLng < tLL_Left.fLng Then ' iLeft = i ' End If ' Next ' Start from leftmost point, keep moving counterclockwise ' until reach the start point again. Log("iP: " & iP & ", iLeft: " & iLeft) Do While True 'iP <> iLeft 'Add current point to result lst_tLLBorder.Add(lst_tLL.Get(iP)) ' Search for a point 'q' such that orientation(p, q, x) is counterclockwise for all points 'x'. iQ = (iP + 1) Mod iPoints For i = 0 To iPoints - 1 'If i is more counterclockwise than current q, then update q 'If Orientation(points(p), points(i), points(q)) = 2 Then If Orientation(lst_tLL.Get(iP), lst_tLL.Get(i), lst_tLL.Get(iQ)) = 2 Then iQ = i End If Next ' Now q is the most counterclockwise with respect to p ' Set p as q for next iteration, so that q is added to result 'hull' iP = iQ Log("iP: " & iP) If iP = iLeft Then Exit End If Loop Return lst_tLLBorder ' Print Result ' For i As Integer = 0 To hull.Count - 1 ' Console.WriteLine("(" & hull(i).x & ", " & hull(i).y & ")") ' Next End Sub
Then it works fine, even with the mirror C shape map points.
The sort is easy to do as the Lat/Lng values come from a SQLite database.
It also avoids finding the lowest value of Lng.
RBS
Bit of improvement by changing this:
B4X:'Find the leftmost point For i = 1 To iPoints - 1
To this:
B4X:'Find the leftmost point For i = 0 To iPoints - 1
Not sure why it was 1 in the C++ code.
RBS
tLL = lst_tLL.Get(i)
tLL_Left = lst_tLL.Get(iLeft)
If tLL.fLng < tLL_Left.fLng Then
Seems to work all fine now and very fast.
This works all fine it seems:I'm finding that it's almost working, except that the left-most point isn't the first element of the Hull list.
Which I think is due to you using a do-while-loop with the test at the top, whereas the C code has the test at the bottom.
Still chewing on that bone. Maybe I'm wrong again.
'Produces convex hull from a set of iPoints points.
'lst_tLL needs to be sorted asc on Lng
'--------------------------------------------------
Sub ConvexHull(lst_tLL As List, iPoints As Int) As List
Dim i As Int
' Dim iLeft As Int
' Dim tLL As TMapLatLng
' Dim tLL_Left As TMapLatLng
Dim iP As Int
Dim iQ As Int
Dim lst_tLLBorder As List
'There must be at least 3 points
If iPoints < 3 Then Return lst_tLLBorder 'return un-initialized list
lst_tLLBorder.Initialize
' 'Find the leftmost point
' For i = 0 To iPoints - 1
' tLL = lst_tLL.Get(i)
' tLL_Left = lst_tLL.Get(iLeft)
' If tLL.fLng < tLL_Left.fLng Then
' iLeft = i
' End If
' Next
' Start from leftmost point, keep moving counterclockwise
' until reach the start point again.
Do While True 'iP <> iLeft
'Add current point to result
lst_tLLBorder.Add(lst_tLL.Get(iP))
' Search for a point 'q' such that orientation(p, q, x) is counterclockwise for all points 'x'.
iQ = (iP + 1) Mod iPoints
For i = 0 To iPoints - 1
'If i is more counterclockwise than current q, then update q
'If Orientation(points(p), points(i), points(q)) = 2 Then
If Orientation(lst_tLL.Get(iP), lst_tLL.Get(i), lst_tLL.Get(iQ)) = 2 Then
iQ = i
End If
Next
' Now q is the most counterclockwise with respect to p
' Set p as q for next iteration, so that q is added to result 'hull'
iP = iQ
Log("iP: " & iP)
If iP = 0 Then
lst_tLLBorder.Add(lst_tLL.Get(0)) 'to close the polygon
Exit
End If
Loop
Return lst_tLLBorder
' Print Result
' For i As Integer = 0 To hull.Count - 1
' Console.WriteLine("(" & hull(i).x & ", " & hull(i).y & ")")
' Next
End Sub
Map points: 1104This works all fine it seems:
B4X:'Produces convex hull from a set of iPoints points. 'lst_tLL needs to be sorted asc on Lng '-------------------------------------------------- Sub ConvexHull(lst_tLL As List, iPoints As Int) As List Dim i As Int ' Dim iLeft As Int ' Dim tLL As TMapLatLng ' Dim tLL_Left As TMapLatLng Dim iP As Int Dim iQ As Int Dim lst_tLLBorder As List 'There must be at least 3 points If iPoints < 3 Then Return lst_tLLBorder 'return un-initialized list lst_tLLBorder.Initialize ' 'Find the leftmost point ' For i = 0 To iPoints - 1 ' tLL = lst_tLL.Get(i) ' tLL_Left = lst_tLL.Get(iLeft) ' If tLL.fLng < tLL_Left.fLng Then ' iLeft = i ' End If ' Next ' Start from leftmost point, keep moving counterclockwise ' until reach the start point again. Do While True 'iP <> iLeft 'Add current point to result lst_tLLBorder.Add(lst_tLL.Get(iP)) ' Search for a point 'q' such that orientation(p, q, x) is counterclockwise for all points 'x'. iQ = (iP + 1) Mod iPoints For i = 0 To iPoints - 1 'If i is more counterclockwise than current q, then update q 'If Orientation(points(p), points(i), points(q)) = 2 Then If Orientation(lst_tLL.Get(iP), lst_tLL.Get(i), lst_tLL.Get(iQ)) = 2 Then iQ = i End If Next ' Now q is the most counterclockwise with respect to p ' Set p as q for next iteration, so that q is added to result 'hull' iP = iQ Log("iP: " & iP) If iP = 0 Then lst_tLLBorder.Add(lst_tLL.Get(0)) 'to close the polygon Exit End If Loop Return lst_tLLBorder ' Print Result ' For i As Integer = 0 To hull.Count - 1 ' Console.WriteLine("(" & hull(i).x & ", " & hull(i).y & ")") ' Next End Sub
RBS
B4X:' 'Find the leftmost point ' For i = 0 To iPoints - 1 ' tLL = lst_tLL.Get(i) ' tLL_Left = lst_tLL.Get(iLeft) ' If tLL.fLng < tLL_Left.fLng Then ' iLeft = i ' End If ' Next ' Start from leftmost point, keep moving counterclockwise ' until reach the start point again. Do While True 'iP <> iLeft 'Add current point to result lst_tLLBorder.Add(lst_tLL.Get(iP))
This works all fine it seems:
It won't be random as the initial index (iP) will be 0 and that will be a point with the lowest value of Lng, so the left-most point and that will be border point.I'm having trouble imagining that working reliably if you're starting from an effectively-random point (which might not be a hull point) rather than the left-most point (which will be a hull point).
It won't be random as the initial index (iP) will be 0 and that will be a point with the lowest value of Lng, so the left-most point and that will be border point.
> Are your input points sorted into Lng order?Are your input points sorted into Lng order? I guess if you can rely on that being the case, then: mission accomplished!
I eventually got your earlier code working with random points, after initialising iP to be iLeft, and moving the do-while-loop test to the bottom.
If the passed tLL list is sorted asc on Lng and asc on Lat (in this order)
> Are you including the sort time in your profiling?Ah, that explains it. I didn't realise you were still sorting your input points. Sorting them got you around not initialising iP to be iLeft. If you're happy remembering to sort the points beforehand, then: "if it ain't broke, don't fix it" sounds good to me too.
Are you including the sort time in your profiling?
Could you post me your Sub ConvexHull?
'Produces convex hull of a set of iPoints points.
Sub ConvexHull(lst_tLL As List, iPoints As Int) As List
Dim i As Int
Dim iLeft As Int
Dim tLL As TMapLatLng
Dim tLL_Left As TMapLatLng
Dim iP As Int
Dim iQ As Int
Dim lst_tLLBorder As List
'There must be at least 3 points
If iPoints < 3 Then Return lst_tLLBorder 'return un-initialized list
lst_tLLBorder.Initialize
'Find the leftmost point
For i = 1 To iPoints - 1
tLL = lst_tLL.Get(i)
tLL_Left = lst_tLL.Get(iLeft)
If tLL.fLng < tLL_Left.fLng Then
iLeft = i
End If
Next
' Start from leftmost point, keep moving counterclockwise
' until reach the start point again.
iP = iLeft 'hidden by obscurity within original c code: int p = l, q;
Do While True
'Add current point to result
lst_tLLBorder.Add(lst_tLL.Get(iP))
' Search for a point 'q' such that orientation(p, q, x) is counterclockwise for all points 'x'.
iQ = (iP + 1) Mod iPoints
For i = 0 To iPoints - 1
'If i is more counterclockwise than current q, then update q
If Orientation(lst_tLL.Get(iP), lst_tLL.Get(i), lst_tLL.Get(iQ)) = 2 Then
iQ = i
End If
Next
' Now q is the most counterclockwise with respect to p
' Set p as q for next iteration, so that q is added to result 'hull'
iP = iQ
If iP = iLeft Then
lst_tLLBorder.Add(lst_tLL.Get(iP)) 'close hull polygon
Exit
End If
Loop
Return lst_tLLBorder
End Sub
Thanks, that works fine indeed without the sort on Lng.No worries. It's been fun. I'm still not sure what happens if two points are on top of each other, especially if they're hull points, so feel free to give that a burl.
B4X:'Produces convex hull of a set of iPoints points. Sub ConvexHull(lst_tLL As List, iPoints As Int) As List Dim i As Int Dim iLeft As Int Dim tLL As TMapLatLng Dim tLL_Left As TMapLatLng Dim iP As Int Dim iQ As Int Dim lst_tLLBorder As List 'There must be at least 3 points If iPoints < 3 Then Return lst_tLLBorder 'return un-initialized list lst_tLLBorder.Initialize 'Find the leftmost point For i = 1 To iPoints - 1 tLL = lst_tLL.Get(i) tLL_Left = lst_tLL.Get(iLeft) If tLL.fLng < tLL_Left.fLng Then iLeft = i End If Next ' Start from leftmost point, keep moving counterclockwise ' until reach the start point again. iP = iLeft 'hidden by obscurity within original c code: int p = l, q; Do While True 'Add current point to result lst_tLLBorder.Add(lst_tLL.Get(iP)) ' Search for a point 'q' such that orientation(p, q, x) is counterclockwise for all points 'x'. iQ = (iP + 1) Mod iPoints For i = 0 To iPoints - 1 'If i is more counterclockwise than current q, then update q If Orientation(lst_tLL.Get(iP), lst_tLL.Get(i), lst_tLL.Get(iQ)) = 2 Then iQ = i End If Next ' Now q is the most counterclockwise with respect to p ' Set p as q for next iteration, so that q is added to result 'hull' iP = iQ If iP = iLeft Then lst_tLLBorder.Add(lst_tLL.Get(iP)) 'close hull polygon Exit End If Loop Return lst_tLLBorder End Sub
> not sure what happens if two points are on top of each otherNo worries. It's been fun. I'm still not sure what happens if two points are on top of each other, especially if they're hull points, so feel free to give that a burl.
B4X:'Produces convex hull of a set of iPoints points. Sub ConvexHull(lst_tLL As List, iPoints As Int) As List Dim i As Int Dim iLeft As Int Dim tLL As TMapLatLng Dim tLL_Left As TMapLatLng Dim iP As Int Dim iQ As Int Dim lst_tLLBorder As List 'There must be at least 3 points If iPoints < 3 Then Return lst_tLLBorder 'return un-initialized list lst_tLLBorder.Initialize 'Find the leftmost point For i = 1 To iPoints - 1 tLL = lst_tLL.Get(i) tLL_Left = lst_tLL.Get(iLeft) If tLL.fLng < tLL_Left.fLng Then iLeft = i End If Next ' Start from leftmost point, keep moving counterclockwise ' until reach the start point again. iP = iLeft 'hidden by obscurity within original c code: int p = l, q; Do While True 'Add current point to result lst_tLLBorder.Add(lst_tLL.Get(iP)) ' Search for a point 'q' such that orientation(p, q, x) is counterclockwise for all points 'x'. iQ = (iP + 1) Mod iPoints For i = 0 To iPoints - 1 'If i is more counterclockwise than current q, then update q If Orientation(lst_tLL.Get(iP), lst_tLL.Get(i), lst_tLL.Get(iQ)) = 2 Then iQ = i End If Next ' Now q is the most counterclockwise with respect to p ' Set p as q for next iteration, so that q is added to result 'hull' iP = iQ If iP = iLeft Then lst_tLLBorder.Add(lst_tLL.Get(iP)) 'close hull polygon Exit End If Loop Return lst_tLLBorder End Sub
I'm still not sure what happens if two points are on top of each other
' To find orientation of ordered triplet (p, q, r).
' The function returns following values
' 0 --> p, q and r are collinear
' 1 --> Clockwise
' 2 --> Counterclockwise
Sub Orientation(tLL1 As TMapLatLng, tLL2 As TMapLatLng, tLL3 As TMapLatLng) As Double
Dim dVal As Double = (tLL2.fLat - tLL1.fLat) * (tLL3.fLng - tLL2.fLng) - (tLL2.fLng - tLL1.fLng) * (tLL3.fLat - tLL2.fLat)
Just one final(?) remark:No worries. It's been fun. I'm still not sure what happens if two points are on top of each other, especially if they're hull points, so feel free to give that a burl.
B4X:'Produces convex hull of a set of iPoints points. Sub ConvexHull(lst_tLL As List, iPoints As Int) As List Dim i As Int Dim iLeft As Int Dim tLL As TMapLatLng Dim tLL_Left As TMapLatLng Dim iP As Int Dim iQ As Int Dim lst_tLLBorder As List 'There must be at least 3 points If iPoints < 3 Then Return lst_tLLBorder 'return un-initialized list lst_tLLBorder.Initialize 'Find the leftmost point For i = 1 To iPoints - 1 tLL = lst_tLL.Get(i) tLL_Left = lst_tLL.Get(iLeft) If tLL.fLng < tLL_Left.fLng Then iLeft = i End If Next ' Start from leftmost point, keep moving counterclockwise ' until reach the start point again. iP = iLeft 'hidden by obscurity within original c code: int p = l, q; Do While True 'Add current point to result lst_tLLBorder.Add(lst_tLL.Get(iP)) ' Search for a point 'q' such that orientation(p, q, x) is counterclockwise for all points 'x'. iQ = (iP + 1) Mod iPoints For i = 0 To iPoints - 1 'If i is more counterclockwise than current q, then update q If Orientation(lst_tLL.Get(iP), lst_tLL.Get(i), lst_tLL.Get(iQ)) = 2 Then iQ = i End If Next ' Now q is the most counterclockwise with respect to p ' Set p as q for next iteration, so that q is added to result 'hull' iP = iQ If iP = iLeft Then lst_tLLBorder.Add(lst_tLL.Get(iP)) 'close hull polygon Exit End If Loop Return lst_tLLBorder End Sub
Thanks for working out that duplicate points are not a problem.I can see that if points 1 and 2, or 2 and 3, are equal (on top of each other), then dVal will return 0.
Still trying to work out what happens if points 1 and 3 are equal.
If points 1 and 3 are equal, then can substitute point 3 for point 1, at which would make the two factors of the first product the same as the two factors of the second product, BUT NEGATIVE, and two negatives make a positive, and thus the two products will be the same, and subtracting same from same will return 0.
So, somewhat serendipitously, the problem of points on top of each other appears to take care of itself. I think worst case is that if you had only three points and they were all the same, then the returned hull would be those same three points in the same order (not that it matters), with 0 length perimeter enclosing 0 area.
B4X:' To find orientation of ordered triplet (p, q, r). ' The function returns following values ' 0 --> p, q and r are collinear ' 1 --> Clockwise ' 2 --> Counterclockwise Sub Orientation(tLL1 As TMapLatLng, tLL2 As TMapLatLng, tLL3 As TMapLatLng) As Double Dim dVal As Double = (tLL2.fLat - tLL1.fLat) * (tLL3.fLng - tLL2.fLng) - (tLL2.fLng - tLL1.fLng) * (tLL3.fLat - tLL2.fLat)
Just one final(?) remark:
Not looked at that and this app (for now?) is only for the UK.You're probably already on to this, but just in case:
watch out for when the point cloud is across the -180..+180 or 360..0 longitude wraparound
View attachment 158836
View attachment 158837
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?