Which points are co-linear & in sequence (i.e. which two points I am between) - vb.net

I have longitude and latitude of my position, and I have a list of positions that is ordered as a list of points(long/lat)) along a road. How do I find out which two points I am between?
If I just search for the two nearest points I will end up with P2 and P3 in my case in the picture.
I want to know how to find out that I'm between point p1 and p2.
The list of points I will search will be database rows containing latitude and longitude so pointers to how to build the sql-query, linq-query or pseudo code, everything that points me to the best solution is welcome. I'm new to geolocation and the math around it so treat me as an newbie. ;)
(The list of points will be ordered so P1 will have an id of 1, p2 will have an id of 2 and so on. )

Bear in mind that what you propose might become really complex (many points under equivalent conditions) and thus delivering an accurate algorithm would require (much) more work. Taking care of simpler situations (like the one in your picture) is not so difficult; you have to include the following:
Convert latitude/longitude values into cartesian coordinates (for ease of calculations; although you might even skip this step). In this link you can get some inspiration on this front; it is in C#, but the ideas are clear anyway.
Iterate through all the available points "by couples" and check whether the point to be analysed (Mypos), falls in the line formed by them, in an intermediate position. As shown in the code below, this calculation is pretty simple and thus you don't need to do any pre-filtering (looking for closer points before).
.
Dim point1() As Double = New Double() {0, 0} 'x,y
Dim point2() As Double = New Double() {0, 3}
Dim pointToCheck() As Double = New Double() {0.05, 2}
Dim similarityRatio As Double = 0.9
Dim minValSimilarDistance As Double = 0.001
Dim similarityDistance As Double = 0.5
Dim eq1 As Double = (point2(0) - point1(0)) * (pointToCheck(1) - point1(1))
Dim eq2 As Double = (point2(1) - point1(1)) * (pointToCheck(0) - point1(0))
Dim maxVal As Double = eq1
If (eq2 > eq1) Then maxVal = eq2
Dim inLine = False
Dim isInBetween As Boolean = False
If (eq1 = eq2 OrElse (maxVal > 0 AndAlso Math.Abs(eq1 - eq2) / maxVal <= (1 - similarityRatio))) Then
inLine = True
ElseIf (eq1 <= minValSimilarDistance AndAlso eq2 <= similarityDistance) Then
inLine = True
ElseIf (eq2 <= minValSimilarDistance AndAlso eq1 <= similarityDistance) Then
inLine = True
End If
If (inLine) Then
'pointToCheck part of the line formed by point1 and point2, but not necessarily between them
Dim insideX As Boolean = False
If (pointToCheck(0) >= point1(0) AndAlso pointToCheck(0) <= point2(0)) Then
insideX = True
Else If (pointToCheck(0) >= point2(0) AndAlso pointToCheck(0) <= point1(0)) Then
insideX = True
End If
if(insideX) Then
If (pointToCheck(1) >= point1(1) AndAlso pointToCheck(1) <= point2(1)) Then
isInBetween = True
ElseIf (pointToCheck(1) >= point2(1) AndAlso pointToCheck(1) <= point1(1)) Then
isInBetween = True
End If
End If
End If
If (isInBetween) Then
'pointToCheck is between point1 and point2
End If
As you can see, I have included various ratios allowing you to tweak the exact conditions (the points will, most likely, not be falling exactly in the line). similarityRatio accounts for "equations" being more or less similar (that is, X and Y values not exactly fitting within the line but close enough). similarityRatio cannot deal properly with cases involving zeroes (e.g., same X or Y), this is what minValSimilarDistance and similarityDistance are for. You can tune these values or just re-define the ratios (with respect to X/Y variations between points, instead of with respect to the "equations").

An equivalent solution in Scala for clarity:
def colinearAndInOrder(a: Point, b: Point, c: Point) = {
lazy val colinear: Boolean =
math.abs((a.lng - b.lng) * (a.lat - c.lat) -
(a.lng - c.lng) * (a.lat - b.lat)) <= 1e-9
lazy val bounded: Boolean =
((a.lat < b.lat && b.lat < c.lat) || (a.lat > b.lat && b.lat > c.lat)) &&
((a.lng < b.lng && b.lng < c.lng) || (a.lng > b.lng && b.lng > c.lng))
close(a,b) || close(b,c) || (colinear && bounded)
}
def close(a: Point, b: Point): Boolean = {
math.abs(a.lat - b.lng) <= 1e-4 && math.abs(a.lat - b.lng) <= 1e-4
}

Related

Visual Basic Loops Even When Condition Isn't Met

I'm trying to program a motorized stage in an xy array configuration in visual basic. I take the n x n array size as input from the user and I move the stages accordingly. Here I am testing a value of 3 so a 3x3 array, The problem occurs with the outer y loop. When County reaches a value of 4, larger than the 3, the loop iterates again, adding another row to my array. Why is it iterating again even though the loop condition isn't satisfied?
Countx and County are my counters and increment every time the stage is moved. Switch is to alternate between the x stage moving back and forth (in a snake pattern)
Dim countx As Integer = 1
Dim county As Integer = 1
Dim switch As Integer = 1
While county <= arraysize
If switch = 1 Then
While countx < arraysize
AxMG17Motor1.MoveJog(0, 1)
countx = countx + 1
Await Task.Delay(5000)
End While
switch = -1
county += 1
AxMG17Motor2.MoveJog(0, 1)
Await Task.Delay(5000)
End If
If switch = -1 Then
While countx > 1
AxMG17Motor1.MoveJog(0, 2)
countx = countx - 1
Await Task.Delay(5000)
End While
switch = 1
county += 1
AxMG17Motor2.MoveJog(0, 1)
Await Task.Delay(5000)
End If
End While
Personally, I prefer absolute moves if possible. You can iterate over the indices and create locations.
I believe you can also be more accurate in allowing the motors to both complete their moves, rather than a "blind" 5 second wait. I am not sure about the thread safety of AxMG17MotorLib moving multiple stages asynchronously, but it's worth a try.
Dim rows = 3
Dim cols = 3
Dim jogSizeX = 5
Dim jogSizeY = 10
Dim originX As Single
Dim originY As Single
AxMG17Motor1.GetPosition(0, originX)
AxMG17Motor2.GetPosition(0, originY)
Dim locations = Enumerable.Range(0, rows - 1).
Select(Function(r) If(r Mod 2 = 0, Enumerable.Range(0, cols - 1).Select(Function(c) New PointF(r, c)),
Enumerable.Range(cols - 1, 0).Select(Function(c) New PointF(r, c)))).
SelectMany(Function(p) p)
For Each location In locations
Dim absoluteX = location.X * jogSizeX + originX
Dim absoluteY = location.Y * jogSizeY + originY
AxMG17Motor1.SetAbsMovePos(0, absoluteX)
AxMG17Motor2.SetAbsMovePos(0, absoluteY)
Await Task.WhenAll({Task.Run(Sub() AxMG17Motor1.MoveAbsolute(0, True)),
Task.Run(Sub() AxMG17Motor2.MoveAbsolute(0, True))})
Console.WriteLine($"Moved to absolute location: ({absoluteX}, {absoluteY}), relative index: ({location.X}, {location.Y})")
Next

Quickest way to determine if a number is a prime number or not VB

I have been working on this for quite a bit now, I have a task of creating a program which calculates if a number entered by a user is prime number or not, the program calculates the time taken and displays this to the user, however I have found two method, one takes more time than the other but it produce accurate numbers, the other one calculates very quickly however it is wrong, I am hoping if someone can help me and tell me the quickest way of calculating this, here are my two codes
Code1:
Dim ch As String
ch = "y"
While ch = "y"
If (num Mod 2 = 0) Then
Console.WriteLine("Is not a prime number!")
Else
Console.WriteLine("Is a prime number!")
End If
Code2:
check = 1 'initilizing a check point to use it in the program to determine prime number
Dim Value As Long
Console.Write(vbLf & "Enter a number To check Whater it is Prime or Not :")
Value = Long.Parse(Console.ReadLine())
start_time = Now
Dim ch As ULong
ch = 0
Dim i As ULong
i = 2
While (i <= Value / 2)
If (Value Mod i = 0) Then
ch = 1
Exit While
End If
i = i + 1
End While
If (ch = 0) Then
Console.WriteLine("Prime Number")
Else
Console.WriteLine("Not Prime Number")
End If
There are a great many prime testers out there, many of them on this site. For checking a single number I use a faster variant of your Code2 with a little extra checking. Here is the pseudocode:
boolean function isPrime(num)
//1, 0 and negatives cannot be prime.
if (num < 2) then
return false
endif
// 2 is the only even prime.
if (num MOD 2 = 0) then
return (num = 2)
endif
// Check for odd factors.
limit <- sqrt(num)
for (factor <- 3; factor <= limit; factor <- factor + 2) do
if (num MOD factor = 0) then
return false
endif
endfor
// If we reach this point then the number is prime.
return true
endfunction
As #user448810 said, you should use the square root of your target number as the limit of your testing loop. You can basically halve the number of tests you do by treating even numbers separately. Once you have taken out the even numbers, then you only have to test odd factors: 3, 5, 7, ...

spacing between two points in 3d cordinate system

i am a bit new to this but I'm trying to create a randomly generated 3d coordinate points with equal spacing, I've tried using for each loop but im confused on how to use in. the purpose is to generate sphere around that point but some sphere are overlapping each other. thanks in advance. the code below is to show how I'm generating the sphere
For i = 0 To noofsp - 1
x = Rnd(1) * maxDist
ws1.Cells(i + 5, 2) = x
y = Rnd(1) * maxDist
ws1.Cells(i + 5, 3) = y
z = Rnd(1) * maxDist
ws1.Cells(i + 5, 4) = z
centers.Add({x, y, z})
Next
You'll need to check the new point against all the other points to make sure that your new point is at a greater distance that the sum of the radii of your new sphere and each sphere you're checking against
You'll need to use pythagoras' theorem to check that the distances and I found the code below from this site. The code on the site is written in c#, but here is the vb.net version.
Public Function Distance3D(x1 As Double, y1 As Double, z1 As Double, x2 As Double, y2 As Double, z2 As Double) As Double
' __________________________________
'd = √ (x2-x1)^2 + (y2-y1)^2 + (z2-z1)^2
'
'Our end result
Dim result As Double
'Take x2-x1, then square it
Dim part1 As Double = Math.Pow((x2 - x1), 2)
'Take y2-y1, then square it
Dim part2 As Double = Math.Pow((y2 - y1), 2)
'Take z2-z1, then square it
Dim part3 As Double = Math.Pow((z2 - z1), 2)
'Add both of the parts together
Dim underRadical As Double = part1 + part2 + part3
'Get the square root of the parts
result = Math.Sqrt(underRadical)
'Return our result
Return result
End Function
To generate the spheres, you would need to expand your code to include checking the new point against all the previously generated points. That code is below with comments.
I have assumed the definition of a variable called minDistance to specify how far apart the centre of the spheres should be. I'm also assuming that all the spheres are the same size. The number should be twice the radius of the spheres
Private Sub GenerateSpheres()
Randomize
For i As Integer = 0 To noofsp - 1
Dim distanceOK As Boolean = False
Dim x, y, z As Integer
'keep generating points until one is found that is
'far enough away. When it is, add it to your data
While distanceOK = False
x = Rnd(1) * maxDist
y = Rnd(1) * maxDist
z = Rnd(1) * maxDist
'If no other points have been generated yet, don't bother
'checking your new point
If centers.Count = 0 Then
distanceOK = True
Else
'If other points exist, loop through the list and check distance
For j As Integer = 0 To centers.Count - 1
'if the point is too close to any other, stop checking,
'exit the For Loop and the While Loop will generate a new
'coordinate for checking, and so on
Dim dist As Integer = Distance3D(centers(j)(0), centers(j)(1), centers(j)(2), x, y, z)
If dist <= minDistance Then
distanceOK = False
'exit the For loop and start the next iteration of the While Loop
Continue While
End If
Next
'If all previous points have been checked none are too close
'flag distanceOK as true
distanceOK = True
End If
End While
'ws1.Cells(i + 5, 2) = x
'ws1.Cells(i + 5, 3) = y
'ws1.Cells(i + 5, 4) = z
centers.Add({x, y, z})
Next
End Sub

VBA: Testing for perfect cubes

I'm trying to write a simple function in VBA that will test a real value and output a string result if it's a perfect cube. Here's my code:
Function PerfectCubeTest(x as Double)
If (x) ^ (1 / 3) = Int(x) Then
PerfectCubeTest = "Perfect"
Else
PerfectCubeTest = "Flawed"
End If
End Function
As you can see, I'm using a simple if statement to test if the cube root of a value is equal to its integer portion (i.e. no remainder). I tried testing the function with some perfect cubes (1, 8, 27, 64, 125), but it only works for the number 1. Any other value spits out the "Flawed" case. Any idea what's wrong here?
You are testing whether the cube is equal to the double supplied.
So for 8 you would be testing whether 2 = 8.
EDIT: Also found a floating point issue. To resolve we will round the decimals a little to try and overcome the issue.
Change to the following:
Function PerfectCubeTest(x As Double)
If Round((x) ^ (1 / 3), 10) = Round((x) ^ (1 / 3), 0) Then
PerfectCubeTest = "Perfect"
Else
PerfectCubeTest = "Flawed"
End If
End Function
Or (Thanks to Ron)
Function PerfectCubeTest(x As Double)
If CDec(x ^ (1 / 3)) = Int(CDec(x ^ (1 / 3))) Then
PerfectCubeTest = "Perfect"
Else
PerfectCubeTest = "Flawed"
End If
End Function
#ScottCraner correctly explains why you were getting incorrect results, but there are a couple other things to point out here. First, I'm assuming that you are taking a Double as input because the range of acceptable numbers is higher. However, by your implied definition of a perfect cube only numbers with an integer cube root (i.e. it would exclude 3.375) need to be evaluated. I'd just test for this up front to allow an early exit.
The next issue you run into is that 1 / 3 can't be represented exactly by a Double. Since you're raising to the inverse power to get your cube root you're also compounding the floating point error. There's a really easy way to avoid this - take the cube root, cube it, and see if it matches the input. You get around the rest of the floating point errors by going back to your definition of a perfect cube as an integer value - just round the cube root to both the next higher and next lower integer before you re-cube it:
Public Function IsPerfectCube(test As Double) As Boolean
'By your definition, no non-integer can be a perfect cube.
Dim rounded As Double
rounded = Fix(test)
If rounded <> test Then Exit Function
Dim cubeRoot As Double
cubeRoot = rounded ^ (1 / 3)
'Round both ways, then test the cube for equity.
If Fix(cubeRoot) ^ 3 = rounded Then
IsPerfectCube = True
ElseIf (Fix(cubeRoot) + 1) ^ 3 = rounded Then
IsPerfectCube = True
End If
End Function
This returned the correct result up to 1E+27 (1 billion cubed) when I tested it. I stopped going higher at that point because the test was taking so long to run and by that point you're probably outside of the range that you would reasonably need it to be accurate.
For fun, here is an implementation of a number-theory based method described here . It defines a Boolean-valued (rather than string-valued) function called PerfectCube() that tests if an integer input (represented as a Long) is a perfect cube. It first runs a quick test which throws away many numbers. If the quick test fails to classify it, it invokes a factoring-based method. Factor the number and check if the multiplicity of each prime factor is a multiple of 3. I could probably optimize this stage by not bothering to find the complete factorization when a bad factor is found, but I had a VBA factoring algorithm already lying around:
Function DigitalRoot(n As Long) As Long
'assumes that n >= 0
Dim sum As Long, digits As String, i As Long
If n < 10 Then
DigitalRoot = n
Exit Function
Else
digits = Trim(Str(n))
For i = 1 To Len(digits)
sum = sum + Mid(digits, i, 1)
Next i
DigitalRoot = DigitalRoot(sum)
End If
End Function
Sub HelperFactor(ByVal n As Long, ByVal p As Long, factors As Collection)
'Takes a passed collection and adds to it an array of the form
'(q,k) where q >= p is the smallest prime divisor of n
'p is assumed to be odd
'The function is called in such a way that
'the first divisor found is automatically prime
Dim q As Long, k As Long
q = p
Do While q <= Sqr(n)
If n Mod q = 0 Then
k = 1
Do While n Mod q ^ k = 0
k = k + 1
Loop
k = k - 1 'went 1 step too far
factors.Add Array(q, k)
n = n / q ^ k
If n > 1 Then HelperFactor n, q + 2, factors
Exit Sub
End If
q = q + 2
Loop
'if we get here then n is prime - add it as a factor
factors.Add Array(n, 1)
End Sub
Function factor(ByVal n As Long) As Collection
Dim factors As New Collection
Dim k As Long
Do While n Mod 2 ^ k = 0
k = k + 1
Loop
k = k - 1
If k > 0 Then
n = n / 2 ^ k
factors.Add Array(2, k)
End If
If n > 1 Then HelperFactor n, 3, factors
Set factor = factors
End Function
Function PerfectCubeByFactors(n As Long) As Boolean
Dim factors As Collection
Dim f As Variant
Set factors = factor(n)
For Each f In factors
If f(1) Mod 3 > 0 Then
PerfectCubeByFactors = False
Exit Function
End If
Next f
'if we get here:
PerfectCubeByFactors = True
End Function
Function PerfectCube(n As Long) As Boolean
Dim d As Long
d = DigitalRoot(n)
If d = 0 Or d = 1 Or d = 8 Or d = 9 Then
PerfectCube = PerfectCubeByFactors(n)
Else
PerfectCube = False
End If
End Function
Fixed the integer division error thanks to #Comintern. Seems to be correct up to 208064 ^ 3 - 2
Function isPerfectCube(n As Double) As Boolean
n = Abs(n)
isPerfectCube = n = Int(n ^ (1 / 3) - (n > 27)) ^ 3
End Function

Skin detection code always shows "Arithmetic overflow"

Why this simple skin detection code always returns Message=Arithmetic operation resulted in an overflow.
xMax = bmp.Width - 1 : yMax = bmp.Height - 1
For y = 0 To yMax
For x = 0 To xMax
tmpColor = fixColor(bmp.GetPixel(x, y))
If (((tmpColor.R > 95) And (tmpColor.G > 40) And (tmpColor.B > 20) And (tmpColor.R - tmpColor.G > 15) And (tmpColor.R > tmpColor.G) And (tmpColor.R > tmpColor.B)) Or _
((tmpColor.R > 220) And (tmpColor.G > 210) And (tmpColor.B > 170) And (tmpColor.R - tmpColor.G <= 15) And (tmpColor.R > tmpColor.B) And (tmpColor.G > tmpColor.B))) Then bmp.SetPixel(x, y, Color.Black)
Next x
Next y
Assuming that tmpColor is defined as System.Color, an educated guess would be that, when this error occurs tmpColor.G is greater than tmpColor.R, which would render the result less than zero, and unable to be stored into a byte.
One possible solution would be to do this
Dim r as integer = tmpColor.R
Dim g as integer = tmpColor.G
Dim b as integer = tmpColor.B
and then use these new values within your calculation. It would make the code cleaner some (and much cleaner if you were to put casting within that if statement).
Another option would be to re-order the tests relying on that subtraction, and use the AndAlso operator:
(tmpColor.R - tmpColor.G > 15) And (tmpColor.R > tmpColor.G)
'To
(tmpColor.R > tmpColor.G) AndAlso (tmpColor.R - tmpColor.G > 15)
AndAlso is VB.Net's short-circuiting logical-and operator, and will cause evaluation of the expression to stop at the first False. As all you use is Ands, replacing them all with AndAlso might see a minor performance increase.
A combination of these two items might make the code more readable, overall.