How to use property value to filter out vertices? - amazon-neptune

I'm trying to filter out vertices to which distance from given latitude and longitude is above max distance stored as vertex property and retrieve the ones that are not filtered.
I used code from Kelvin Lawrence's book to calculate the distance using the Haversine Great Circle Distance formula. The thing is, if I pass an integer value to lte everything seems to be working fine, but I need to feed it with each traversed vertex property value.
Code:
r_deg = 0.017453293
e_rad = 6371
def get_points_within_distance(latitude, longitude, r_deg, e_rad):
try:
_result = n_graph. \
with_side_effect("r_deg", r_deg). \
with_side_effect("e_rad", e_rad). \
with_side_effect("p_lat", latitude). \
with_side_effect("p_lon", longitude). \
V(). \
hasLabel("point"). \
where(
project("ladiff", "lodiff", "latitude", "longitude").
by(project("lat").by("latitude").
math("(lat - p_lat) * r_deg")).
by(project("lon").by("longitude").
math("(lon - p_lon) * r_deg")).
by("latitude").
by("longitude").
math('(sin(ladiff/2))^2 + cos(latitude*r_deg) * cos(p_lat*r_deg) * (sin(lodiff/2))^2').
math('e_rad * (2 * asin(sqrt(_)))').
is_(lte(10))
). \
value_map(True). \
to_list()
except Exception as _e:
LOG.error(f"Failed to retrieve points: {_e}.")
return
return _result
Each vertex has three properties:
latitude
longitude
max_distance
What I'm trying to achieve is to pass max_distance value instead of 10.

You'll need to use the where()-by() approach here. Instead of using the where() as part of the calculation, we'll use an as() label to bookmark our starting vertex. Then perform the distance calculation and use the where()-by() to filter. After the where()-by(), we'll refer back to the starting vertex via a select() on the label for the traversals that passed the filter.
n_graph.
with_side_effect("r_deg", r_deg).
with_side_effect("e_rad", e_rad).
with_side_effect("p_lat", latitude).
with_side_effect("p_lon", longitude).
V().
hasLabel("point").
as("startPoint").
project("ladiff", "lodiff", "latitude", "longitude").
by(project("lat").by("latitude").
math("(lat - p_lat) * r_deg")).
by(project("lon").by("longitude").
math("(lon - p_lon) * r_deg")).
by("latitude").
by("longitude").
math('(sin(ladiff/2))^2 + cos(latitude*r_deg) * cos(p_lat*r_deg) * (sin(lodiff/2))^2').
math('e_rad * (2 * asin(sqrt(_)))').
where(lte("startPoint")).by().by("max_distance").
select("startPoint").
value_map(True).
to_list()

Related

How to extend the polygon to a certain distance?

How to extend the polygon to a certain distance?
I create a convex hull around the multipoint. But I need to extend the range to several kilometers. At least in theory.
http://img.radiokot.ru/files/21274/1oykzc5pez.png
Assuming that you're able to get a convex hull (which maybe you're using ConvexHullAggregate!), STBuffer() should do what you want.
declare #hull geography = «your value here»;
select #hull.STBuffer(10000); -- 10 km buffer
NB: the 10000 may need to change based on the SRID that you're using since SRIDs have units of distance baked into them inherently. But SRID 4326 is what's used in the docs most often and the native unit for that SRID is meters. So 10 km → 10000 m.
Build outer bisector vector in every vertex (as sum of normalized normals na and nb of two neighbor edges) and normalize it
bis = na + nb
bis = bis / Length(bis)
Make length of bisector to provide needed distance as
l = d / Cos(fi/2)
where d is offset, and fi is angle between vectors na and nb.
fi = atan2(crossproduct(na,nb), dotproduct(na,nb))
or without trigonometric functions:
l = d / Sqrt((1 + dotproduct(na,nb))/2)
And find offset polygon vertex:
P' = P + l * bis

Generate normally distributed series using BIgQuery

Is there a way to generate normally distributed series in BQ? ideally specifying the mean and sd of the distribution.
I found a way using Marsaglia polar method , but it is not ideal for I do not want polar coordinates of the distribution but to generate an array that follows the parameters specified for it to be normally distributed.
Thank you in advance.
This query gives you the euclidean coordinates of the normal distribution centred in 0. You can adjust both the mean (mean variable) or the sd (variance variable) and the x-axis values (GENERATE_ARRAY(beginning,end,step)) :
CREATE TEMPORARY FUNCTION normal(x FLOAT64)
RETURNS FLOAT64
LANGUAGE js AS """
var mean=0;
var variance=1;
var x0=1/(Math.sqrt(2*Math.PI*variance));
var x1=-Math.pow(x-mean,2)/(2*Math.pow(variance,2));
return x0*Math.pow(Math.E,x1);
""";
WITH numbers AS
(SELECT x FROM UNNEST(GENERATE_ARRAY(-10, 10,0.5)) AS x)
SELECT x, normal(x) as normal
FROM numbers;
For doing that, I used "User Defined Funtions" [1]. They are used when you want to have another SQL expression or when you want to use Java Script (as I did).
NOTE: I used the probability density function of the normal distribution, if you want to use another you'd need to change variables x0,x1 and the return (I wrote them separately so it's clearer).
Earlier answers give the probability distribution function of a normal rv. Here I modify previous answers to give a random number generated with the desired distribution, in BQ standard SQL, using the 'polar coordinates' method. The question asks not to use polar coordinates, which is an odd request, since polar coordinates are not use in the generation of the normally distributed random number.
CREATE TEMPORARY FUNCTION rnorm ( mu FLOAT64, sigma FLOAT64 ) AS
(
(select mu + sigma*(sqrt( 2*abs(
log( RAND())
)
)
)*cos( 2*ACOS(-1)*RAND())
)
)
;
select
num ,
rnorm(-1, 5.3) as RAND_NORM
FROM UNNEST(GENERATE_ARRAY(1, 17) ) AS num
The easiest way to do it in BQ is by creating a custom function:
CREATE OR REPLACE FUNCTION
`your_project.functions.normal_distribution_pdf`
(x ANY TYPE, mu ANY TYPE, sigma ANY TYPE) AS (
(
SELECT
safe_divide(1,sigma * power(2 * ACOS(-1),0.5)) * exp(-0.5 * power(safe_divide(x-mu,sigma),2))
)
);
Next you only need to apply the function:
with inputs as (
SELECT 1 as x, 0 as mu, 1 as sigma
union all
SELECT 1.5 as x, 1 as mu, 2 as sigma
union all
SELECT 2 as x , 2 as mu, 3 as sigma
)
SELECT x,
`your_project.functions.normal_distribution_pdf`(x, mu, sigma) as normal_pdf
from
inputs

Calculating distance between 2 points fails when points are the same

I am running following SQL query in my JAVA Spring server. This query works perfect for almost all coordinates except for one specific pair c = <23.065079, 72.511478> (= to_lat, to_long):
SELECT *
FROM karpool.ride
WHERE Acos(Sin(Radians(23.065079)) * Sin(Radians(to_lat)) +
Cos(Radians(23.065079)) * Cos(Radians(to_lat)) *
Cos(Radians(to_lon) - Radians(72.511478))) * 6371 <= 10;
My database has many locations within 10 km distance to c. With the above query, I get all those locations' distances, except for the one which exactly matches with c. The distance returned should be 0 in that case, but the query fails.
Is this an SQL issue or is there something wrong with the formula?
This is most probably due to floating point accuracy problems.
First of all, the used formula is the Great circle distance formula:
Let φ1,λ1 and φ1,λ2 be the geographical latitude and longitude of two points 1 and 2, and Δφ,Δλ their absolute differences; then Δσ, the central angle between them, is given by the spherical law of cosines:
Δσ = arccos ( sin φ1 ∙ sin φ2 + cos φ1 ∙ cos φ2 ∙ cos (Δλ) ).
The distance d, i.e. the arc length, for a sphere of radius r and Δσ given in radians
d = r Δσ.
Now if the two points are the same, then Δλ = 0, and thus cos(Δλ) = cos(0) = 1, and the first formula reduces to:
Δσ = arccos (sin φ ∙ sin φ + cos φ ∙ cos φ).
The argument to arccos has become the Pythagorean trigonometric identity, and thus equals 1.
So the above reduces to:
Δσ = arccos (1).
The problem
The domain of the arccosine is: −1 ≤ x ≤ 1, so with the value 1 we are at the boundary of the domain.
As the value of 1 was the result of several floating point operations (sines, cosines, multiplications), it could occur that the value is not exactly 1, but something like 1.0000000000004. That poses a problem, for that value is out of range for calculating the arccosine. Database engines respond differently to this situation:
SQL Server will raise an exception:
An invalid floating point operation occurred.
MySql will just evaluate the expression as null.
The solution
Somehow the argument passed to the arccosine should be made to stay in the range −1 ≤ x ≤ 1. One way of doing this, is to round the argument to a number of decimals that is large enough to keep some precision, but small enough to round away any excess outside this range caused by floating point operations.
Most database engines have a round function to which a second argument can be provided to specify the number of digits to keep, and so the SQL would look like this (keeping 6 decimals):
SELECT *
FROM karpool.ride
WHERE Acos(Round(
Sin(Radians(23.065079)) * Sin(Radians(to_lat)) +
Cos(Radians(23.065079)) * Cos(Radians(to_lat)) *
Cos(Radians(to_lon) - Radians(72.511478)),
6
)) * 6371 <= 10;
Alternatively, you could use the functions greatest and least, which some database engines provide, to turn any excess value to 1 (or -1):
SELECT *
FROM karpool.ride
WHERE Acos(Greatest(Least(
Sin(Radians(23.065079)) * Sin(Radians(to_lat)) +
Cos(Radians(23.065079)) * Cos(Radians(to_lat)) *
Cos(Radians(to_lon) - Radians(72.511478)),
1), -1)
) * 6371 <= 10;
Note that SQL Server does not provide greatest/least functions. A question to overcome this has several answers.

Smooth Coloring Mandelbrot Set Without Complex Number Library

I've coded a basic Mandelbrot explorer in C#, but I have those horrible bands of color, and it's all greyscale.
I have the equation for smooth coloring:
mu = N + 1 - log (log |Z(N)|) / log 2
Where N is the escape count, and |Z(N)| is the modulus of the complex number after the value has escaped, it's this value which I'm unsure of.
My code is based off the pseudo code given on the wikipedia page: http://en.wikipedia.org/wiki/Mandelbrot_set#For_programmers
The complex number is represented by the real values x and y, using this method, how would I calculate the value of |Z(N)| ?
|Z(N)| means the distance to the origin, so you can calculate it via sqrt(x*x + y*y).
If you run into an error with the logarithm: Check the iterations before. If it's part of the Mandelbrot set (iteration = max_iteration), the first logarithm will result 0 and the second will raise an error.
So just add this snippet instead of your old return code. .
if (i < iterations)
{
return i + 1 - Math.Log(Math.Log(Math.Sqrt(x * x + y * y))) / Math.Log(2);
}
return i;
Later, you should divide i by the max_iterations and multiply it with 255. This will give you a nice rgb-value.

Plotting point perpendicular to line half way through cocos2d

So I have a line that is plotted between two points. Lets say A and B, I can grab the mid point of the line in Cocos2d really easily and I also can calculate the vector and the perpendicular vector to this line quite easily. However my math skills are very rusty and I have no idea how to do the following.
Lets say the distance between A and B is 50, so the midpoint is 25. I would like to plot a point that is perpendicular to this line with a distance of 10 away from it.
C
/ \
/ \
/ \
/ \
/ \
A------------B
Sorry for the terrible example, but I'm not sure how to do this. Also the AB line is always at some angle, it's never straight like it is here.
Given the midpoint m and the perpendicular vector v, you need to normalize v and then move in the direction of v from m. So something like this:
Vector2d nv = v / v.length(); // Assuming Vector2d is your vector class and length gives the length of v
Point2d newPoint = m + (nv * 10.0); // Assumes you can multiply a vector by a scalar
If you aren't working in C++, you may have to write it manually like this:
Vector2d nv;
nv.x = v.x / v.length();
nv.y = v.y / v.length();
newPoint.x = m.x + nv.x * 10.0;
newPoint.y = m.y + nv.y * 10.0;