I tried to rotate a geometry around its local axis, but haven't found a way to do so. I know that there is ST_Rotate (see https://postgis.net/docs/ST_Rotate.html) for 2D calculations and (among others) ST_RotateX (see https://postgis.net/docs/ST_RotateX.html), but these methods rotate a geometry around the origin. I also tried to abuse ST_Affine when I tried to change (what seems to be) the origin (namely 0/0/0):
SELECT ST_Affine(
ST_GeomFromText(ST_AsText(runway_area)),
1, 0, 0, 0,
cos(rotRadians), -sin(rotRadians), 0, sin(rotRadians), cos(rotRadians),
--- use the geometry's centroid instead of 0, 0, 0
ST_X(ST_GeomFromText(ST_AsText(runway_area))), ST_Y(ST_GeomFromText(ST_AsText(runway_area))), ST_Z(ST_GeomFromText(ST_AsText(runway_area)))
)
It didn't work out - all I got was something that was way away from the intended location. Do I miss a very fundamental method by PostGIS here to rotate a geometry around one of its local axis?
Take a closer look at ST_Rotate, and note that there are three argument signatures:
geometry ST_Rotate(geometry geomA, float rotRadians);
geometry ST_Rotate(geometry geomA, float rotRadians, float x0, float y0);
geometry ST_Rotate(geometry geomA, float rotRadians, geometry pointOrigin);
The x0 and y0 or pointOrigin arguments for the last two signatures do what your questions asks: they allow the rotation to pivot around a custom defined coordinate.
And an example from the docs show how to rotate a geometry 60 degrees clockwise around the centroid:
SELECT ST_AsEWKT(ST_Rotate(geom, radians(-60.0), ST_Centroid(geom)))
FROM (SELECT 'LINESTRING (50 160, 50 50, 100 50)'::geometry AS geom) AS foo;
st_asewkt
--------------------------------------------------------------
LINESTRING(116.4225 130.6721,21.1597 75.6721,46.1597 32.3708)
(1 row)
You could combine ST_Rotate with ST_Centroid, example:
ST_Rotate(ST_GeomFromText(ST_AsText(runway_area)), -pi()/3, ST_Centroid(ST_GeomFromText(ST_AsText(runway_area)))
The last three arguments to ST_Affine do not represent the origin, but the global "shift" in the affine transformation. The documentation for ST_RotateX shows how to use this function to generate a rotation around the x-axis. All these parameters are zero since it's a rotation without translation.
If you want to employ a general axis, you would need to construct the corresponding rotation matrix and substitute its elements for the arguments a,b,c,d,e,f,g,h,i of ST_Affine and set xoff, yoff, zoff to zero.
Related
This Question was posted, but never answered.
Similar to This Question, I am trying to understand SCNNode.rotation as a 4D vector. The prior question utilizes an example that only manipulates 1 axis, i.e.,
SCNNode.rotation = (0, 0, 1, degToRad(45)) //Rotate about z-axis by 45 degrees
which makes sense; however, what if I wanted to rotate the X axis by 20 degrees, Y axis by 45 degrees and then Z axis by 78 degrees?
SCNNode.rotation = ??
I would provide code I've tried, but I don't understand conceptually the notion of a 4D rotation vector.
Every node just has a transform with 4x4 matrix. So all the rotation operations are reflecting in the changing the transform.
In this case, if you change either of rotation, eulerAngles and orientation, you are supposed to get same value.
If rotating about three axises, I suggested using eulerAngles.
node.eulerAnges = SCNVector3(x:degToRad(20),y:degToRad(45), z:degToRad(78))
After you set this, go back and check to value of rotation:
SCNVector4(x: -0.16975601, y: 0.5943193, z: 0.786109, w: 1.448788)
This means there is an axis going through point(-0.16975601, 0.5943193, 0.786109) and origin (0,0,0), and node is rotating around it for 1.448788 (82 degree).
I'm using PostgresSQL 9.5 to generate a rectangle (geometric type BOX).
That works fine
SELECT Box(Point(-50, -100), Point(50, 100)) ; -- this works
Then I try to rotate that box around the origin (its center point). The rotation function is both
* Scaling/rotation box '((0,0),(1,1))' * point '(2.0,0)'
/ Scaling/rotation box '((0,0),(2,2))' / point '(2.0,0)'
where the x-point is the scaling factor (2.0 in this example) and the y-point is the rotation radians (0 in this example).
To check that the rotation is correct, I calculate the height, width and area of the box for each angle.
SELECT
xx.deg, -- angle in degrees
xx.geom, -- geometry of box
Area(xx.geom),
Center(xx.geom),
Height(xx.geom),
Width(xx.geom)
FROM
(SELECT deg,
Box(Point(-5, -10), Point(5, 10)) / Point(1, Radians(deg)) -- scale box by factor 1 and rotate by radians(degrees)
AS geom
FROM Generate_series(0, 360, 90) AS deg -- generate list of degrees from 0 to 360 by 90
) xx;
The results, which don't change between using * or / functions,
deg;geom;area;center;height;width
0;"(5,10),(-5,-10)";200;"(0,0)";20;10
90;"(5.97218570021291,0.618912639168559),(-5.97218570021291,-0.618912639168559)";14.785044853294;"(0,0)";1.23782527833712;11.9443714004258
180;"(3.35025316397424,0.525130727607429),(-3.35025316397424,-0.525130727607429)";7.03728352666753;"(0,0)";1.05026145521486;6.70050632794848
270;"(2.24607945852279,0.584400089411209),(-2.24607945852279,-0.584400089411209)";5.25043614554159;"(0,0)";1.16880017882242;4.49215891704558
360;"(1.67575357650576,0.529070250354662),(-1.67575357650576,-0.529070250354662)";3.5463654570185;"(0,0)";1.05814050070932;3.35150715301153
show that the box is being rotated but also scaled - the height, width and area are all not constant. I read somewhere that a rotation needs to take into account scaling, but I don't understand what scaling factor should be used to compsenate for the rotation. The documentation doesn't give any examples, and most of the resources online are for PostGIS (i.e. ST_Rotate).
UPDATE
I have a working solution that is not the fastest but gives correct results. See here
https://stackoverflow.com/a/39680955/2327328
TL/DR: You cannot rotate boxes
The two operators * and / can be confusing. The idea is that they treat the two dimensional points as complex numbers and perform multiply (or divide) them as complex numbers. So for example point '(2,3)' * point '(1,-1)' returns (5,1) because (2+3i)*(1-i)=5+i or point '(0,1)' * point '(0,1)' returns (-1,0) because i*i=-1.
So if you want to use * to rotate by an angle say φ, you have to multiply by the complex number exp(i*φ) which is equal to cos(φ)+i*sin(φ). For example:
SELECT point '(1,0)' * point(cos(radians(45)),sin(radians(45)));
=> (0.707106781186548,0.707106781186547)
rotates the point (1,0) by 45 degrees counter clockwise.
Unfortunately, this doesn't work very well with boxes. If you do
SELECT box '((0,0),(1,1))' * point(cos(radians(45)),sin(radians(45)));
=> (1.11022302462516e-16,1.41421356237309),(0,0)
which means that postgres rotated the two points as individual points and not the whole box. The problem is that a box is a rectangle with sides parallel to the x and y axes. By that definition, if you rotate a box by 45 degrees, what you get is not a box. So you cannot rotate boxes.
In theory, it should be possible to rotate polygons. Unfortunatelly, it seems that this has not been implemented (yet?) in postgresql:
$ SELECT polygon(box '((0,0),(1,1))') * point(1,0);
ERROR: operator does not exist: polygon * point
LINE 1: SELECT polygon(box '((0,0),(1,1))') * point(1,0);
I have an issue with rotating an a node multiple times. I am working on a game with a rolling ball, and while I can rotate the ball along one axis, or two axis by the same amount, I cannot rotate at partial angles.
example:
// Roll right 90 -
SCNNode.pivot = SCNMatrix4MakeRotation(Float(M_PI_2), 0, 1, 0)
// Roll right 180 -
SCNNode.pivot = SCNMatrix4MakeRotation(Float(M_PI_2) * 2, 0, 1, 0)
// Roll up 90 -
SCNNode.pivot = SCNMatrix4MakeRotation(Float(M_PI_2), 1, 0, 0)
// Roll up & right 90 -
SCNNode.pivot = SCNMatrix4MakeRotation(Float(M_PI_2), 1, 1, 0)
All of which will work, however if I need to roll ball right 180 and up 90 I'm stuck.
Even if there was some way to add the vectors together that would do me.
Any help greatly appreciated.
To combine the effects of rotation matrices, use matrix multiplication.
To do that in SceneKit, you can either:
Create separate rotation matrices and multiply them together using SCNMatrix4Mult.
Apply a rotation directly to an existing matrix using SCNMatrix4Rotate. (This is equivalent to the SCNMatrix4MakeRotation + SCNMatrix4Mult option; it just combines those steps into a single function call.)
If the order of transformations is important to your app, remember that matrix multiplication order is the reverse of transformation order.
I want to draw UIimage with CGAffineTransform but It gives wrong output with CGContextConcatCTM
I have try with below code :
CGAffineTransform t = CGAffineTransformMake(1.67822, -1.38952, 1.38952, 1.67822, 278.684, 209.129); // transformation of uiimageview
UIGraphicsBeginImageContext(CGSizeMake(1024, 768));
CGContextRef imageContext = UIGraphicsGetCurrentContext();
CGContextDrawImage(imageContext, dragView.frame, dragView.image.CGImage);
CGContextConcatCTM(imageContext, t);
NSLog(#"\n%#\n%#", NSStringFromCGAffineTransform(t),NSStringFromCGAffineTransform(CGContextGetCTM(imageContext)));
Output :
[1.67822, -1.38952, 1.38952, 1.67822, 278.684, 209.129] // imageview transformation
[1.67822, 1.38952, 1.38952, -1.67822, 278.684, 558.871] // drawn image transformation
CGAffineTransform CGAffineTransformMake (
CGFloat a,
CGFloat b,
CGFloat c,
CGFloat d,
CGFloat tx,
CGFloat ty
);
Parameter b, d and ty changed, How to solve this?
There is no problem to solve. Your log output is correct.
Comparing the two matrixes, the difference between the two is this:
scale vertically by -1 (which affects two of the first four members)
translate vertically by 349.742 (which affects the last member)
I'm going to take a guess and say your view is about 350 points tall. Am I right? Actually, the 349.742 is weird, since you set the context's height to 768. It's almost half (perhaps because the anchor point is centered?), but well short, and cutting off the status bar wouldn't make sense here (and wouldn't account for a 68.516-point difference). So that is a puzzle. But, what follows is still true:
A vertical scale and translate is how you would flip a context. This context has gone from lower-left origin to upper-left origin, or vice versa.
That happened before you concatenated your (unexplained, hard-coded) matrix in. Assuming you didn't flip the context yourself, it probably came that way (I would guess as a UIKit implementation detail).
Concatenation (as in CGContextConcatCTM) does not replace the old transformation matrix with the new one; it is matrix multiplication. The matrix you have afterward is the product of both the matrix you started with and the one you concatenated onto it. The resulting matrix is both flipped and then… whatever your matrix does.
You can see this for yourself by simply getting the CTM before you concatenate your matrix onto it, and logging that. You should see this:
[0, -1, 0, -1, 0, 349.742]
See also “The Math Behind the Matrices” in the Quartz 2D Programming Guide.
i am working on some camera data. I have some points which consist of azimuth, angle, distance, and of course coordinate field attributes. In postgresql postgis I want to draw shapes like this with functions.
how can i draw this pink range shape?
at first should i draw 360 degree circle then extracting out of my shape... i dont know how?
I would create a circle around the point(x,y) with your radius distance, then use the info below to create a triangle that has a larger height than the radius.
Then using those two polygons do an ST_Intersection between the two geometries.
NOTE: This method only works if the angle is less than 180 degrees.
Note, that if you extend the outer edges and meet it with a 90 degree angle from the midpoint of your arc, you have a an angle, and an adjacent side. Now you can SOH CAH TOA!
Get Points B and C
Let point A = (x,y)
To get the top point:
point B = (x + radius, y + (r * tan(angle)))
to get the bottom point:
point C = (x + radius, y - (r * tan(angle)))
Rotate your triangle to you azimouth
Now that you have the triangle, you need to rotate it to your azimuth, with a pivot point of A. This means you need point A at the origin when you do the rotation. The rotation is the trickiest part. Its used in computer graphics all the time. (Actually, if you know OpenGL you could get it to do the rotation for you.)
NOTE: This method rotates counter-clockwise through an angle (theta) around the origin. You might have to adjust your azimuth accordingly.
First step: translate your triangle so that A (your original x,y) is at 0,0. Whatever you added/subtracted to x and y, do the same for the other two points.
(You need to translate it because you need point A to be at the origin)
Second step: Then rotate points B and C using a rotation matrix. More info here, but I'll give you the formula:
Your new point is (x', y')
Do this for points B and C.
Third step: Translate them back to the original place by adding or subtracting. If you subtracted x last time, add it this time.
Finally, use points {A,B,C} to create a triangle.
And then do a ST_Intersection(geom_circle,geom_triangle);
Because this takes a lot of calculations, it would be best to write a program that does all these calculations and then populates a table.
PostGIS supports curves, so one way to achieve this that might require less math on your behalf would be to do something like:
SELECT ST_GeomFromText('COMPOUNDCURVE((0 0, 0 10), CIRCULARSTRING(0 10, 7.071 7.071, 10 0), (10 0, 0 0))')
This describes a sector with an origin at 0,0, a radius of 10 degrees (geographic coordinates), and an opening angle of 45°.
Wrapping that with additional functions to convert it from a true curve into a LINESTRING, reduce the coordinate precision, and to transform it into WKT:
SELECT ST_AsText(ST_SnapToGrid(ST_CurveToLine(ST_GeomFromText('COMPOUNDCURVE((0 0, 0 10), CIRCULARSTRING(0 10, 7.071 7.071, 10 0), (10 0, 0 0))')), 0.01))
Gives:
This requires a few pieces of pre-computed information (the position of the centre, and the two adjacent vertices, and one other point on the edge of the segment) but it has the distinct advantage of actually producing a truly curved geometry. It also works with segments with opening angles greater than 180°.
A tip: the 7.071 x and y positions used in the example can be computed like this:
x = {radius} cos {angle} = 10 cos 45 ≈ 7.0171
y = {radius} sin {angle} = 10 sin 45 ≈ 7.0171
Corner cases: at the antimeridian, and at the poles.