From d60efbc12c99a82f9b5e090d25115f6952c4fcce Mon Sep 17 00:00:00 2001 From: Regina Obe Date: Wed, 4 Oct 2017 16:19:29 +0000 Subject: [PATCH] =?utf8?q?ST=5FAngle=20function=20from=20R=C3=A9mi=20Cura?= =?utf8?q?=20Closes=20https://github.com/postgis/postgis/pull/97=20Closes?= =?utf8?q?=20#3876?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit git-svn-id: http://svn.osgeo.org/postgis/trunk@15884 b70326c6-7e19-0410-871a-916f4a2858ee --- NEWS | 2 +- doc/reference_measure.xml | 66 ++++++++++++++++ postgis/lwgeom_functions_basic.c | 126 +++++++++++++++++++++++++++++++ postgis/postgis.sql.in | 19 +++++ regress/lwgeom_regress.sql | 34 +++++++++ regress/lwgeom_regress_expected | 11 +++ 6 files changed, 257 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 6b7a8335f..56ec4bfe5 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,7 @@ PostGIS 2.5.0 2018/xx/xx * New Features * - + - #3876, ST_Angle function (Rémi Cura) PostGIS 2.4.0 2017/09/30 diff --git a/doc/reference_measure.xml b/doc/reference_measure.xml index bf855a158..c376f02ac 100644 --- a/doc/reference_measure.xml +++ b/doc/reference_measure.xml @@ -819,6 +819,72 @@ SELECT degrees(ST_Azimuth(ST_Point(25, 45), ST_Point(75, 100))) AS degA_B, , , , PostgreSQL Math Functions + + + + + ST_Angle + + Returns the angle between 3 points, or between 2 vectors (4 points or 2 lines). + + + + + float ST_Angle + geometry point1 + geometry point2 + geometry point3 + geometry point4 + + + float ST_Angle + geometry line1 + geometry line2 + + + + + Description + + For 3 points, computes the angle measured clockwise of P1P2P3. + If input are 2 lines, get first and last point of the lines as 4 points. + For 4 points,compute the angle measured clockwise of P1P2,P3P4. + Results are always positive, between 0 and 2*Pi radians. + + Uses azimuth of pairs or points. + + ST_Angle(P1,P2,P3) = ST_Angle(P2,P1,P2,P3) + Result is in radian and can be converted to degrees using a built-in PostgreSQL function degrees(), as shown in the example. + Availability: 2.5.0 + + + + Examples + Geometry Azimuth in degrees + + WITH rand AS ( + SELECT s, random() * 2 * PI() AS rad1 + , random() * 2 * PI() AS rad2 + FROM generate_series(1,2,2) AS s + ) + , points AS ( + SELECT s, rad1,rad2, ST_MakePoint(cos1+s,sin1+s) as p1, ST_MakePoint(s,s) AS p2, ST_MakePoint(cos2+s,sin2+s) as p3 + FROM rand + ,cos(rad1) cos1, sin(rad1) sin1 + ,cos(rad2) cos2, sin(rad2) sin2 + ) + SELECT s, ST_AsText(ST_SnapToGrid(ST_MakeLine(ARRAY[p1,p2,p3]),0.001)) AS line + , degrees(ST_Angle(p1,p2,p3)) as computed_angle + , round(degrees(2*PI()-rad2 -2*PI()+rad1+2*PI()))::int%360 AS reference + , round(degrees(2*PI()-rad2 -2*PI()+rad1+2*PI()))::int%360 AS reference + FROM points ; + +1 | line | computed_angle | reference +------------------+------------------ +1 | LINESTRING(1.511 1.86,1 1,0.896 0.005) | 155.27033848688 | 155 + + + diff --git a/postgis/lwgeom_functions_basic.c b/postgis/lwgeom_functions_basic.c index 0f9d6ac97..a81794f7e 100644 --- a/postgis/lwgeom_functions_basic.c +++ b/postgis/lwgeom_functions_basic.c @@ -102,6 +102,7 @@ Datum LWGEOM_setpoint_linestring(PG_FUNCTION_ARGS); Datum LWGEOM_asEWKT(PG_FUNCTION_ARGS); Datum LWGEOM_hasBBOX(PG_FUNCTION_ARGS); Datum LWGEOM_azimuth(PG_FUNCTION_ARGS); +Datum LWGEOM_angle(PG_FUNCTION_ARGS); Datum LWGEOM_affine(PG_FUNCTION_ARGS); Datum LWGEOM_longitude_shift(PG_FUNCTION_ARGS); Datum optimistic_overlap(PG_FUNCTION_ARGS); @@ -2420,6 +2421,131 @@ Datum LWGEOM_azimuth(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(result); } +/** + * Compute the angle defined by 3 points or the angle between 2 vectors + * defined by 4 points + * given Point geometries. + * @return NULL on exception (same point). + * Return radians otherwise (always positive). + */ +PG_FUNCTION_INFO_V1(LWGEOM_angle); +Datum LWGEOM_angle(PG_FUNCTION_ARGS) +{ + GSERIALIZED * seri_geoms[4]; + LWGEOM *geom_unser; + LWPOINT *lwpoint; + POINT2D points[4]; + double az1,az2 ; + double result; + int srids[4]; + int i = 0 ; + int j = 0; + int err_code = 0; + int n_args = PG_NARGS(); + + /* no deserialize, checking for common error first*/ + for(i=0; i0) + switch (err_code){ + default: /*always executed*/ + for (j=0;j<=i;j++) + PG_FREE_IF_COPY(seri_geoms[j], j); + + case 1: + lwpgerror("Empty geometry"); + PG_RETURN_NULL() ; + break; + + case 2: + lwpgerror("Argument must be POINT geometries"); + PG_RETURN_NULL(); + break; + + case 3: + lwpgerror("Operation on mixed SRID geometries"); + PG_RETURN_NULL(); + break; + } + /* extract points */ + for(i=0; ipoint, 0, &points[i]) ) + { + /* // can't free serialized geom, it might be needed by lw + for (j=0;j