diff --git a/Makefile b/Makefile index dca3b7d..63e4e80 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,8 @@ DATA_built = $(RELEASE_SQL) \ DOCS = README.pg_sphere COPYRIGHT.pg_sphere REGRESS = init tables points euler circle line ellipse poly path box index \ - contains_ops contains_ops_compat bounding_box_gist gnomo epochprop + contains_ops contains_ops_compat bounding_box_gist gnomo epochprop \ + spherepoint_array_center ifneq ($(USE_HEALPIX),0) REGRESS += healpix moc mocautocast diff --git a/doc/functions.sgm b/doc/functions.sgm index 30118bf..acac332 100644 --- a/doc/functions.sgm +++ b/doc/functions.sgm @@ -143,7 +143,7 @@ - + <type>strans</type> functions diff --git a/doc/operators.sgm b/doc/operators.sgm index 7162a01..18b9873 100644 --- a/doc/operators.sgm +++ b/doc/operators.sgm @@ -393,8 +393,10 @@ The center operator <literal>@@</literal> is a non-boolean unary operator returning the center of an object. In the current implementation of <application>pgSphere</application>, - only centers of circles and ellipses are supported. Instead - of using the operator, you can use the function + only centers of circles, ellipses, points, arrays of points are + </para> + <para> + supported. Instead of using the operator, you can use the function <literal>center(object)</literal>. </para> <example> @@ -403,6 +405,73 @@ <![CDATA[sql> SELECT @@ scircle '<(0d,20d),30d>';]]> </programlisting> </example> + <example> + <title>Center array of points + + SELECT @@ ARRAY[ + spoint(40.7128, -74.0060), + spoint(34.0522, -118.2437), + spoint(37.7749, -122.4194) +] AS center;]]> + + + + + + + + + Center of a spoint + + For the completeness of the interface, the function returns the point itself + + + select @@ spoint(0,0) AS spoint;]]> + + + + + + + + +Basic example array of points + + SELECT center(ARRAY[ + spoint(40.7128, -74.0060), + spoint(34.0522, -118.2437), + spoint(37.7749, -122.4194) +]);]]> + + + + + + + + + Behavior with empty array of points + + SELECT center('{}');]]> + + + + + + + + Example with opposite array points(poles) + + SELECT center(ARRAY[ + spoint(0, 10), + spoint(0, -10), +]);]]> + + + + + + diff --git a/expected/init_test.out.in b/expected/init_test.out.in index 129283c..b81d094 100644 --- a/expected/init_test.out.in +++ b/expected/init_test.out.in @@ -24,12 +24,12 @@ psql:pg_sphere.test.sql:159: NOTICE: argument type spath is only a shell psql:pg_sphere.test.sql:178: NOTICE: type "sbox" is not yet defined DETAIL: Creating a shell type definition. psql:pg_sphere.test.sql:185: NOTICE: argument type sbox is only a shell -psql:pg_sphere.test.sql:8540: NOTICE: type "spherekey" is not yet defined +psql:pg_sphere.test.sql:8570: NOTICE: type "spherekey" is not yet defined DETAIL: Creating a shell type definition. -psql:pg_sphere.test.sql:8547: NOTICE: argument type spherekey is only a shell -psql:pg_sphere.test.sql:8561: NOTICE: type "pointkey" is not yet defined +psql:pg_sphere.test.sql:8577: NOTICE: argument type spherekey is only a shell +psql:pg_sphere.test.sql:8591: NOTICE: type "pointkey" is not yet defined DETAIL: Creating a shell type definition. -psql:pg_sphere.test.sql:8568: NOTICE: argument type pointkey is only a shell -psql:pg_sphere.test.sql:8574: NOTICE: argument type pointkey is only a shell -psql:pg_sphere.test.sql:8580: NOTICE: argument type pointkey is only a shell -psql:pg_sphere.test.sql:8586: NOTICE: argument type pointkey is only a shell +psql:pg_sphere.test.sql:8598: NOTICE: argument type pointkey is only a shell +psql:pg_sphere.test.sql:8604: NOTICE: argument type pointkey is only a shell +psql:pg_sphere.test.sql:8610: NOTICE: argument type pointkey is only a shell +psql:pg_sphere.test.sql:8616: NOTICE: argument type pointkey is only a shell diff --git a/expected/init_test_healpix.out.in b/expected/init_test_healpix.out.in index 330dc68..26b2c58 100644 --- a/expected/init_test_healpix.out.in +++ b/expected/init_test_healpix.out.in @@ -1,2 +1,2 @@ -psql:pg_sphere.test.sql:9153: NOTICE: return type smoc is only a shell -psql:pg_sphere.test.sql:9159: NOTICE: argument type smoc is only a shell +psql:pg_sphere.test.sql:9183: NOTICE: return type smoc is only a shell +psql:pg_sphere.test.sql:9189: NOTICE: argument type smoc is only a shell diff --git a/expected/points.out b/expected/points.out index 84375e6..7793c0b 100644 --- a/expected/points.out +++ b/expected/points.out @@ -648,3 +648,10 @@ SELECT '( 0h 2m 30s , -90d 0m 0s)'::spoint<->'( 12h 2m 30s , -90d 0m 0s)'::spoin 0 (1 row) +-- Center operator ------------------- +select @@ spoint(0,0) AS spoint; + spoint +--------- + (0 , 0) +(1 row) + diff --git a/expected/spherepoint_array_center.out b/expected/spherepoint_array_center.out new file mode 100644 index 0000000..5fe3273 --- /dev/null +++ b/expected/spherepoint_array_center.out @@ -0,0 +1,73 @@ +SELECT center(ARRAY[ + spoint(40.7128, -74.0060), + spoint(34.0522, -118.2437), + spoint(37.7749, -122.4194) +]); + center +---------------------------------------- + (3.04366980631979 , 0.858938068921891) +(1 row) + +SELECT center('{}'::SPoint[]); + center +-------- + +(1 row) + +CREATE FUNCTION spoint_from_xyz(FLOAT8, FLOAT8, FLOAT8) + RETURNS spoint + AS 'pg_sphere', 'spoint_from_xyz' + LANGUAGE 'c' + IMMUTABLE STRICT PARALLEL SAFE; +SELECT spoint_from_xyz(1, 0, 0); + spoint_from_xyz +----------------- + (0 , 0) +(1 row) + +SELECT spoint_from_xyz(0, 0, 0); + spoint_from_xyz +----------------- + (0 , 0) +(1 row) + +SELECT center(ARRAY[ + spoint_from_xyz(1, 0, 0), + spoint_from_xyz(-1, 0, 0) +]); + center +----------------------- + (1.5707963267949 , 0) +(1 row) + +SELECT center(NULL::SPoint[]); + center +-------- + +(1 row) + +SELECT @@ ARRAY[ + spoint(40.7128, -74.0060), + spoint(34.0522, -118.2437), + spoint(37.7749, -122.4194) +] AS center; + center +---------------------------------------- + (3.04366980631979 , 0.858938068921891) +(1 row) + +SELECT @@ ARRAY[]::spoint[] AS center; + center +-------- + +(1 row) + +SELECT @@ ARRAY[ + spoint_from_xyz(1, 0, 0), + spoint_from_xyz(-1, 0, 0) +] AS center; + center +----------------------- + (1.5707963267949 , 0) +(1 row) + diff --git a/pgs_point.sql.in b/pgs_point.sql.in index a9afb84..a1480aa 100644 --- a/pgs_point.sql.in +++ b/pgs_point.sql.in @@ -167,3 +167,33 @@ CREATE OPERATOR <-> ( COMMENT ON OPERATOR <-> (spoint, spoint) IS 'distance between spherical points'; +CREATE FUNCTION center(dots_vector SPoint[]) + RETURNS spoint + AS 'MODULE_PATHNAME', 'center' + LANGUAGE 'c' + IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR @@ ( + RIGHTARG = SPoint[], + PROCEDURE = center +); + +COMMENT ON OPERATOR @@ (NONE , SPoint[]) IS + 'Return gravity center from array of spherical points'; + +CREATE FUNCTION center(spoint) + RETURNS spoint + AS 'MODULE_PATHNAME', 'spherepoint_center' + LANGUAGE 'c' + IMMUTABLE STRICT PARALLEL SAFE; + +COMMENT ON FUNCTION center(spoint) IS + 'For the correctness of the interface, the function returns the spoint itself'; + +CREATE OPERATOR @@ ( + RIGHTARG = SPoint, + PROCEDURE = center +); + +COMMENT ON OPERATOR @@ (NONE , SPoint) IS + 'For the correctness of the interface, the function returns the spoint itself'; diff --git a/sql/points.sql b/sql/points.sql index a2d53a6..542c240 100644 --- a/sql/points.sql +++ b/sql/points.sql @@ -234,3 +234,5 @@ SELECT '( 0h 2m 30s , 90d 0m 0s)'::spoint<->'( 12h 2m 30s , 90d 0m 0s)'::spoint; SELECT '( 0h 2m 30s , -90d 0m 0s)'::spoint<->'( 12h 2m 30s , -90d 0m 0s)'::spoint; +-- Center operator ------------------- +select @@ spoint(0,0) AS spoint; diff --git a/sql/spherepoint_array_center.sql b/sql/spherepoint_array_center.sql new file mode 100644 index 0000000..9d4d3ff --- /dev/null +++ b/sql/spherepoint_array_center.sql @@ -0,0 +1,37 @@ +SELECT center(ARRAY[ + spoint(40.7128, -74.0060), + spoint(34.0522, -118.2437), + spoint(37.7749, -122.4194) +]); + +SELECT center('{}'::SPoint[]); + +CREATE FUNCTION spoint_from_xyz(FLOAT8, FLOAT8, FLOAT8) + RETURNS spoint + AS 'pg_sphere', 'spoint_from_xyz' + LANGUAGE 'c' + IMMUTABLE STRICT PARALLEL SAFE; + +SELECT spoint_from_xyz(1, 0, 0); + +SELECT spoint_from_xyz(0, 0, 0); + +SELECT center(ARRAY[ + spoint_from_xyz(1, 0, 0), + spoint_from_xyz(-1, 0, 0) +]); + +SELECT center(NULL::SPoint[]); + +SELECT @@ ARRAY[ + spoint(40.7128, -74.0060), + spoint(34.0522, -118.2437), + spoint(37.7749, -122.4194) +] AS center; + +SELECT @@ ARRAY[]::spoint[] AS center; + +SELECT @@ ARRAY[ + spoint_from_xyz(1, 0, 0), + spoint_from_xyz(-1, 0, 0) +] AS center; diff --git a/src/point.c b/src/point.c index 5132835..b997f94 100644 --- a/src/point.c +++ b/src/point.c @@ -5,6 +5,7 @@ PG_FUNCTION_INFO_V1(spherepoint_in); PG_FUNCTION_INFO_V1(spherepoint_from_long_lat); PG_FUNCTION_INFO_V1(spherepoint_distance); +PG_FUNCTION_INFO_V1(spherepoint_center); PG_FUNCTION_INFO_V1(spherepoint_long); PG_FUNCTION_INFO_V1(spherepoint_lat); PG_FUNCTION_INFO_V1(spherepoint_x); @@ -12,6 +13,9 @@ PG_FUNCTION_INFO_V1(spherepoint_y); PG_FUNCTION_INFO_V1(spherepoint_z); PG_FUNCTION_INFO_V1(spherepoint_xyz); PG_FUNCTION_INFO_V1(spherepoint_equal); +PG_FUNCTION_INFO_V1(spoint_from_xyz); +PG_FUNCTION_INFO_V1(center); + bool spoint_eq(const SPoint *p1, const SPoint *p2) @@ -179,6 +183,13 @@ spoint_dist(const SPoint *p1, const SPoint *p2) } } +Datum +spherepoint_center(PG_FUNCTION_ARGS) +{ + SPoint *p = (SPoint *) PG_GETARG_POINTER(0); + PG_RETURN_POINTER(p); +} + Datum spherepoint_distance(PG_FUNCTION_ARGS) { @@ -263,3 +274,59 @@ spherepoint_equal(PG_FUNCTION_ARGS) PG_RETURN_BOOL(spoint_eq(p1, p2)); } + +Datum spoint_from_xyz(PG_FUNCTION_ARGS) +{ + Vector3D point_coords; + SPoint *p = (SPoint *) palloc(sizeof(SPoint)); + + point_coords.x = PG_GETARG_FLOAT8(0); + point_coords.y = PG_GETARG_FLOAT8(1); + point_coords.z = PG_GETARG_FLOAT8(2); + vector3d_spoint(p, &point_coords); + + if (p == NULL) + { + PG_RETURN_NULL(); + } + + spoint_check(p); + PG_RETURN_POINTER(p); +} + +Datum center(PG_FUNCTION_ARGS) +{ + int num_elements, i; + SPoint * p = (SPoint *) palloc(sizeof(SPoint)); + SPoint * array_data; + Vector3D v; + Vector3D point_coords = {0,0,0}; + ArrayType *dots_vector; + + dots_vector = PG_GETARG_ARRAYTYPE_P(0); + num_elements = ArrayGetNItems(ARR_NDIM(dots_vector), ARR_DIMS(dots_vector)); + if (num_elements <= 0) + { + PG_RETURN_NULL(); + } + + array_data = (SPoint *) ARR_DATA_PTR(dots_vector); + + for (i = 0; i < num_elements; i++) + { + spoint_vector3d(&v, &array_data[i]); + point_coords.x += v.x; + point_coords.y += v.y; + point_coords.z += v.z; + } + + point_coords.x /= num_elements; + point_coords.y /= num_elements; + point_coords.z /= num_elements; + + vector3d_spoint(p, &point_coords); + + spoint_check(p); + + PG_RETURN_POINTER(p); +} diff --git a/src/point.h b/src/point.h index 76c2202..9c4984c 100644 --- a/src/point.h +++ b/src/point.h @@ -15,6 +15,11 @@ typedef struct float8 lat; /* latitude value in radians */ } SPoint; +/* + * For the correctness of the interface, the function returns the spoint itself + */ +Datum spherepoint_center(PG_FUNCTION_ARGS); + /* * Calculate the distance between two spherical points in radians. */ @@ -90,4 +95,9 @@ Datum spherepoint_xyz(PG_FUNCTION_ARGS); */ Datum spherepoint_equal(PG_FUNCTION_ARGS); +/* + * Return gravity center from array of spherical points + */ +Datum center(PG_FUNCTION_ARGS); + #endif