Can I use the Postgres functions to find points inside a rotating rectangle of fixed size? - sql

I'm using Postgres 9.5 and I've just installed PostGIS for some extended functions. I have a table with (x,y) points and I want to find the rectangle that fits the maximum number of points. The constraint is that the rectangle side lenghts are fixed. So far I'm counting how many points are in the box without rotation. My points are centered around the origin, (0,0).
SELECT Sum(CASE
WHEN x > -5
AND x < 5
AND y > -10
AND y < 10 THEN 1
ELSE 0
END) AS inside_points,
Count(1) AS total_points
FROM track_t;
This query gives me the count of points inside a rectangle with origin (0,0) and lenghts x = 10 and y = 20.
From here I would create a helper table of rotated rectangle corner points (angle, x1, y1, x2, y2), then cross join to my data, and count over the points per angle, while GROUP BY angle. Then I can select which angle gives me the most points inside the rectangle.
But this seems a little old fashioned, and perhaps non-performant. Additionally, counting points inside a rotated rectangle is not a trivial calculation.
Are there more efficient and elegant ways, perhaps using Postgres Geometric Datatypes or PostGIS Box2D, to rotate a rectangle with fixed side lenghts, and then to count the number of points inside? The geometric functions look good, but they seem to provide minimum bounding boxes and not the other way around.
In addition to Postgresql, I'm using a Python framework that could be used in case SQL can't make this work.
Update: One thing I tried is to use Geometric Types, specifically BOX
SELECT deg, Box(Point(-5, -10), Point(5, 10)) * Point(1, Radians(deg))
FROM Generate_series(0, 360, 90) AS deg
Unforunately, the Rotate function by a Point doesn't work for Polygons.

I ended up by generating rectangle vertices, rotating those vertices, and then comparing the area of the rectangle (constant) with the area of the 4 triangles that are made by including the test point.
This technique is based on the parsimonious answer:
Make triangle. Suppose, abcd is the rectangle and x is the point then if area(abx)+area(bcx)+area(cdx)+area(dax) equals area(abcd) then the point is inside it.
The rectangles are defined by
A bottom left (-x/2,-y/2)
B top left (-x/2,+y/2)
C top right (+x/2,+y/2)
D bottom right (+x/2,-y/2)
This code then checks if point (qx,qy) is inside a rectangle of width x=10 and height y=20, which is rotated around the origin (0,0) by an angle with range of 0 to 180, by 10 degrees.
Here's the code. It's taking 9 minutes to check 750k points, so there is definite room for improvement. Additionally, It can be parallelized once I upgrade to 9.6
with t as (select 10*0.5 as x, 20*0.5 as y, 17.0 as qx, -3.0 as qy)
select
z.angle
-- ABC area
--,abs(0.5*(z.ax*(z.by-z.cy)+z.bx*(z.cy-z.ay)+z.cx*(z.ay-z.by)))
-- CDA area
--,abs(0.5*(z.cx*(z.dy-z.ay)+z.dx*(z.ay-z.cy)+z.ax*(z.cy-z.dy)))
-- ABCD area
,abs(0.5*(z.ax*(z.by-z.cy)+z.bx*(z.cy-z.ay)+z.cx*(z.ay-z.by))) + abs(0.5*(z.cx*(z.dy-z.ay)+z.dx*(z.ay-z.cy)+z.ax*(z.cy-z.dy))) as abcd_area
-- ABQ area
--,abs(0.5*(z.ax*(z.by-z.qx)+z.bx*(z.qy-z.ay)+z.qx*(z.ay-z.by)))
-- BCQ area
--,abs(0.5*(z.bx*(z.cy-z.qx)+z.cx*(z.qy-z.by)+z.qx*(z.by-z.cy)))
-- CDQ area
--,abs(0.5*(z.cx*(z.dy-z.qx)+z.dx*(z.qy-z.cy)+z.qx*(z.cy-z.dy)))
-- DAQ area
--,abs(0.5*(z.dx*(z.ay-z.qx)+z.ax*(z.qy-z.dy)+z.qx*(z.dy-z.ay)))
-- total area of triangles with question point (ABQ + BCQ + CDQ + DAQ)
,abs(0.5*(z.ax*(z.by-z.qx)+z.bx*(z.qy-z.ay)+z.qx*(z.ay-z.by)))
+ abs(0.5*(z.bx*(z.cy-z.qx)+z.cx*(z.qy-z.by)+z.qx*(z.by-z.cy)))
+ abs(0.5*(z.cx*(z.dy-z.qx)+z.dx*(z.qy-z.cy)+z.qx*(z.cy-z.dy)))
+ abs(0.5*(z.dx*(z.ay-z.qx)+z.ax*(z.qy-z.dy)+z.qx*(z.dy-z.ay))) as point_area
from
(
SELECT
a.id as angle
-- bottom left (A)
,(-t.x) * cos(radians(a.id)) - (-t.y) * sin(radians(a.id)) as ax
,(-t.x) * sin(radians(a.id)) + (-t.y) * cos(radians(a.id)) as ay
--top left (B)
,(-t.x) * cos(radians(a.id)) - (t.y) * sin(radians(a.id)) as bx
,(-t.x) * sin(radians(a.id)) + (t.y) * cos(radians(a.id)) as by
--top right (C)
,(t.x) * cos(radians(a.id)) - (t.y) * sin(radians(a.id)) as cx
,(t.x) * sin(radians(a.id)) + (t.y) * cos(radians(a.id)) as cy
--bottom right (D)
,(t.x) * cos(radians(a.id)) - (-t.y) * sin(radians(a.id)) as dx
,(t.x) * sin(radians(a.id)) + (-t.y) * cos(radians(a.id)) as dy
-- point to check (Q)
,t.qx as qx
,t.qy as qy
FROM generate_series(0,180,10) AS a(id), t
) z
;
the results then are
angle;abcd_area;point_area
0;200;340
10;200;360.6646055963
20;200;373.409049054212
30;200;377.846096908265
40;200;373.84093170467
50;200;361.515248361426
60;200;341.243556529821
70;200;313.641801308188
80;200;279.548648061772
90;200;240
*100;200;200*
*110;200;200*
*120;200;200*
*130;200;200*
*140;200;200*
150;200;237.846096908265
160;200;277.643408923024
170;200;312.04311584956
180;200;340
Where the rotations of angles 100, 110, 120, 130 and 140 degrees then includes the test-point (indicated with *)

Related

Calculate the rotation between 2 Inertial Measurement Unit (IMU) for each Tait-Bryan angles

The first IMU (called S1) is placed on the shoulder and works as a reference; the other (S2) is placed on the arm. They provide the quaternion of their rotation relative to the absolute reference (magnetic north and gravity vector). The simple idea is that I need to show the Yaw, Pitch and Roll differences between these two (e.g. an ideal abduction/adduction movement should have pitch contributions only). I started using quaternions, by calculating the rotation between the two (conj(q1) * q2) and then converting it to YPR angles by using:
# Rotation quaternion q1 = (w,x,y,z)
# unit = sum of squared elements (sqx = x^2, etc.)
yaw = math.atan2(2 * q1[2] * q1[0] - 2 * q1[1] * q1[3], sqx - sqy - sqz + sqw)
pitch = math.asin(2 * (q1[1] * q1[2] + q1[3] * q1[0]) / sqx + sqy + sqz + sqw)
roll = math.atan2(2 * q1[1] * q1[0] - 2 * q1[2] * q1[3], -sqx - sqy + sqz + sqw)
but this doesn't work in my case, since pitch and roll are not consistent in different arm positions. E.g. if relative Yaw is 90 deg, pitch and roll angles are interchanged. E.g. if I apply a pitch rotation to S2, it appears to be a roll rotation (since the rotation is on the y-axis for the reference sensor S1).
How can I avoid this?
Should I simply convert both quaternion to YPR angles and then calculate the difference of each pair (without using the difference between quaternions)? Maybe the "rotation" approach is not correct, since I don't need the inverse transformation but only the actual rotation for each axis?

Rotation of a unit vector at a random point iby an angle along Y axis in 3D space

I had attached also the schematic to depict my question.
I need to rotate the vector V with the base point P by an angle and find the new vector V'.
The rotation axis is say for is about a local y axis at point P (which is parallel to global Y axis)
Subsequently, I need to rotate the initial vector V about x axis which is parallel to global Y axis.
The main reason for the rotation is to find the new vector V' at point P. Both the rotations are independent and each of the rotation provides a new V'. I'm programming this in VB.net and output is a double() of new vector V'.
Just apply the two rotations independently (see Wikipedia). The base point does not play any role in this because it is just a constant offset that never changes. If I got your description right, you want the following:
//rotation about y-axis
iAfterRot1 = cos(phi1) * i + sin(phi1) * k
jAfterRot1 = j
kAfterRot1 = -sin(phi1) * i + cos(phi) * k
//rotation about x-axis
iAfterRot2 = iAfterRot1
jAfterRot2 = cos(phi2) * jAfterRot1 - sin(phi2) * kAfterRot1
kAfterRot2 = sin(phi2) * jAfterRot1 + cos(phi2) * kAfterRot1

Determine angle of a straight line in 3D space

I have a straight line in space with an start and end point (x,y,z) and I am attempting to get the angle between this vector and the plane defined by z=0. I am using VB.NET
Here is a picture of the line in my 3d environment (the line I'm intersted in is circled in red) :
It is set to an angle of 70 degrees right now.
You need 2 rays to define an angle.
If you want the angle between a vector and a plane, it is defined for any vector in that plane. However, there is only one minimal value for that, which is the angle between a vector and its projection onto said plane.
Therefore, that minimal value is the one we take when we speak of the angle between a vector and a plane.
This value is also π/2 - the angle between your vector and the the vector that is normal to the plane.You can read more about it all on this site.
With v your vector (thus v.x = end.x - start.x and idem for y and z), n the normal to the plane and a the angle you are looking for, we know from the definition of a scalar product that:
<v,n> = ||v|| * ||n|| * cos(π/2 - a)
We know cos(π/2 - a) = sin(a), and the normal to the z=0 plane is simply the vector n = (0, 0, 1). Thus both the scalar product, v.x * n.x + v.y * n.y + v.z * n.z, and the norm of n, ||n|| = 1, can be simplified a lot. We get the following expression:
sin(a) = v.z / ||v||
Thus finally, the formula by taking the reciprocical of the sine and expliciting the norm of v:
a = Asin(v.z / sqrt( v.x*v.x + v.y*v.y + v.z*v.z ))
According to this documentation the Asin function exists in your System.Math class. It does, however, return the value in radians:
Return Value
Type: System.Double
An angle, θ, measured in radians, such that -π/2 ≤ θ ≤ π/2
-or-
NaN if d < -1 or d > 1 or d equals NaN.
Luckily the same System.Math class contains the value of π so that you can do the conversion:
a *= 180 / Math.PI

Equation to find average of multiple velocities?

I need to find the average Edit: total 2D velocity given multiple 2D velocities (speed and direction). A few examples:
Example 1
Velocity 1 is 90° at a speed of 10 pixels or units per second.
Velocity 2 is 270° at a speed of 5 pixels or units per second.
The average velocity is 90° at 5 pixels or units per second.
Example 2
Velocity 1 is 0° at a speed of 10 pixels or units per second
Velocity 2 is 180° at a speed of 10 pixels or units per second
Velocity 3 is 90° at a speed of 8 pixels or units per second
The average velocity is 90° at 8 pixels or units per second
Example 3
Velocity 1 is 0° at 10 pixels or units per second
Velocity 2 is 90° at 10 pixels or units per second
The average velocity is 45° at 14.142 pixels or units per second
I am using JavaScript but it's mostly a language-independent question and I can convert it to JavaScript if necessary.
If you're going to be using a bunch of angles, I would just calculate each speed,
vx = v * cos(theta),
vy = v * sin(theta)
then sum the x velocities and the y velocities separately as vector components and divide by the total number of velocities,
sum(vx) / total v, sum(vy) / total v
and then finally calculate the final speed and direction with your final vx and vy. The magnitude of the speed can be found by a simple application of pythagorean theorem, and then final angle should just be tan-1(y/x).
Per example #3
vx = 10 * cos(90) + 10 * cos(0) = 10,
vy = 10 * sin(90) + 10 * sin(0) = 10
so, tan-1(10/10) = tan-1(1) = 45
then a final magnitude of sqrt(10^2 + 10^2) = 14.142
These are vectors, and you should use vector addition to add them. So right and up are positive, while left and down are negative.
Add your left-to-right vectors (x axis).
Example 1 = -10+5 = -5
Example 2 = -8 = -8
Example 3 = 10 = 10. (90 degrees is generally 90 degrees to the right)
Add you ups and downs similarly and you get these velocities, your left-to-right on the left in the brackets, and your up-to-down on the right.
(-5, 0)
(-8,0)
(10, 10)
These vectors contain all the information you need to plot the motion of an object, you do not need to calculate angles to plot the motion of the object. If for some reason you would rather use speeds (similar to velocity, but different) and angles, then you must first calculate the vectors as above and then use the Pythagorean theorem to find the speed and simple trigonometry to get the angle. Something like this:
var speed = Math.sqrt(x * x + y * y);
var tangeant = y / x;
var angleRadians = Math.atan(tangeant);
var angleDegrees = angleRadians * (180 / Math.PI);
I'll warn you that you should probably talk to someone who know trigonometry and test this well. There is potential for misleading bugs in work like this.
From your examples it sounds like you want addition of 2-dimensional vectors, not averages.
E.g. example 2 can be represented as
(0,10) + (0,-10) + (-8, 0) = (-8,0)
The speed is then equal to the length of the vector:
sqrt(x^2+y^2)
To get average:
add each speed, and then divide by the number of speeds.
10mph + 20mph / 2 = 15
12mph + 14mph + 13mph + 16mph / 4 = 14 (13,75)
This is not so much average as it is just basic vector addition. You're finding multiple "pixel vectors" and adding them together. If you have a velocity vector of 2 pixels to the right, and 1 up, and you add it to a velocity vector of 3 pixels to the left and 2 down, you will get a velocity vector of 1 pixel left, and 1 down.
So the speed is defined by
pij = pixels going up or (-)down
pii = pixels going right or (-)left
speedi = pii1 + pii2 = 2-3 = -1 (1 pixel left)
speedj = pij1 + pij2 = 1-2 = -1 (1 pixel down)
From there, you need to decide which directions are positive, and which are negative. I recommend that left is negative, and down is negative (like a mathematical graph).
The angle of the vector, would be the arctan(speedj/speedi)
arctan(-1/-1) = 45 degrees

How determine if point is within rectangle given all latitude/longitude coordinates?

If given x/y coordinates for all 4 corners of rectangle, and then another x/y, it's easy to determine if the point is within the rectangle if top left is 0,0.
But what if the coordinates are latitude/longitude where they can be negative (please see attached). Is there a formula that can work in this case?
Mathematicaly, you could use inequations to determine that.
edit: When doing the example, i've noticed you put the coordinates in the inverse format (y,x) instead of (x,y). In my example I use (x,y) format, so I just inverted the order to easy my explanation.
Let's say
A = (-130,10)
B = (-100,20)
C = (-125,-5)
D = (-100,5)
You build an inequation from your rectangle edges :
if( (x,y) < AB && (x,y) > AC && (x,y) > CD && (x,y) < BD) then
(x,y) belongs to rectangle ABCD
end if
If all inequations are true, then your point belongs to the rectangle
Concrete example :
AB represent the segment but can be represented by a formula : y = ax + b
to determine a (the slope of the formula, not the point A) you get the difference of
(Ay - By) / (Ax - Bx)
Ay means Y component of point A wich is 10 in that case
That formula gives us
(10 - 20) / (-130 - -100) = -10 / -30 = 1/3
Now we have
y = x/3 + b
We now determine b. We now that both point A and B belongs to that formula. So we take any of them to replace the x,y values in the formula. Let's take point B :
20 = -100/3 + b
We isolate b giving us :
b = -100 / 60 = -10/6
We have now
y = x/3 - (6/10)
So if we want to determine if Point Z (10, 15) belongs to your retangle, you check firstly if
y > x/3 - (10/6)
Then in the case of Z(10, 15) :
15 > 10/3 - (10/6)
15 > 10/6
15 > 1.66 is true
So condition is met for this edge. You need to this same logic for each edges.
Note that to determine if you use > or <, you need to tell if at a certain x value, our point has a bigger y or smaller y value than our rectangle edge.
You can use < and > if you want a point to be strictly inside the rectangle; <= and >= if a point on the rectangle's edge belongs to the rectangle too. You decide.
I hope that my explanation is clear. Feel free to ask more if some points are unclear.