From 0fcfa3631972c84b83baf856eda07b5e31eb42e8 Mon Sep 17 00:00:00 2001 From: Ed Sabol Date: Sat, 28 Oct 2023 18:55:00 -0400 Subject: [PATCH] Add function to create spoly from an array of numbers in radians. --- expected/poly.out | 26 ++++++ pgs_polygon.sql.in | 41 ++++++---- sql/poly.sql | 14 ++++ src/polygon.c | 82 ++++++++++++++++--- src/polygon.h | 9 +- .../pg_sphere--1.3.1--1.3.2.sql.in | 17 ++++ 6 files changed, 160 insertions(+), 29 deletions(-) diff --git a/expected/poly.out b/expected/poly.out index 50e510e..2d622b5 100644 --- a/expected/poly.out +++ b/expected/poly.out @@ -318,6 +318,18 @@ SELECT spoly '{(10d,0d),(10d,1d),(15d,0d)}'; {(10d , 0d),(10d , 1d),(15d , 0d)} (1 row) +SELECT spoly(ARRAY[0.017453292519943295, 0.03490658503988659, 0.05235987755982988, 0.06981317007977318, 0.08726646259971647, 0.10471975511965977]); + spoly +--------------------------------- + {(1d , 2d),(3d , 4d),(5d , 6d)} +(1 row) + +SELECT spoly(ARRAY[0.17453292519943295, 0.0, 0.17453292519943295, 0.017453292519943295, 0.2617993877991494, 0.0]); + spoly +------------------------------------ + {(10d , 0d),(10d , 1d),(15d , 0d)} +(1 row) + SELECT spoly_deg(ARRAY[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]); spoly_deg --------------------------------- @@ -335,8 +347,22 @@ SELECT spoly '{(10d,0d),(10d,1d)}'; ERROR: spherepoly_in: more than two points needed LINE 1: SELECT spoly '{(10d,0d),(10d,1d)}'; ^ +SELECT spoly(ARRAY[1.0, 2.0, 3.0, 4.0, 5.0]); +ERROR: spherepoly_rad: invalid number of arguments (must be even and >= 6) +SELECT spoly(ARRAY[1.0, 2.0, 3.0, NULL, 5.0, 6.0]); +ERROR: spherepoly_rad: input array is invalid because it has null values +SELECT spoly(ARRAY[]::float8[]); +ERROR: spherepoly_rad: invalid number of arguments (must be even and >= 6) +SELECT spoly(NULL::float8[]); + spoly +------- + +(1 row) + SELECT spoly_deg(ARRAY[1.0, 2.0, 3.0, 4.0, 5.0]); ERROR: spherepoly_deg: invalid number of arguments (must be even and >= 6) +SELECT spoly_deg(ARRAY[1.0, 2.0, 3.0, NULL, 5.0, 6.0]); +ERROR: spherepoly_deg: input array is invalid because it has null values SELECT spoly_deg(ARRAY[]::float8[]); ERROR: spherepoly_deg: invalid number of arguments (must be even and >= 6) SELECT spoly_deg(NULL::float8[]); diff --git a/pgs_polygon.sql.in b/pgs_polygon.sql.in index fdd4277..3a5d801 100644 --- a/pgs_polygon.sql.in +++ b/pgs_polygon.sql.in @@ -1,6 +1,8 @@ - +-- ************************** +-- -- spherical polygon functions - +-- +-- ************************** CREATE FUNCTION npoints(spoly) RETURNS INT4 @@ -933,6 +935,29 @@ COMMENT ON OPERATOR !&& (sellipse, spoly) IS 'true if spherical ellipse does not overlap spherical polygon'; +-- +-- Functions to create a polygon from arrays +-- + +CREATE FUNCTION spoly(float8[]) + RETURNS spoly + AS 'MODULE_PATHNAME', 'spherepoly_rad' + LANGUAGE 'c' + IMMUTABLE STRICT PARALLEL SAFE; + +COMMENT ON FUNCTION spoly(float8[]) IS + 'creates spoly from array of numbers in radians'; + +CREATE FUNCTION spoly_deg(float8[]) + RETURNS spoly + AS 'MODULE_PATHNAME', 'spherepoly_deg' + LANGUAGE 'c' + IMMUTABLE STRICT PARALLEL SAFE; + +COMMENT ON FUNCTION spoly_deg(float8[]) IS + 'creates spoly from array of numbers in degrees'; + + -- -- Aggregate functions to add points to polygon -- @@ -946,18 +971,6 @@ CREATE FUNCTION spoly_add_point_aggr (spoly, spoint) COMMENT ON FUNCTION spoly_add_point_aggr (spoly, spoint) IS 'adds a spherical point to spherical polygon. Do not use it standalone!'; -CREATE FUNCTION spoly_deg(float8[]) - RETURNS spoly - AS 'MODULE_PATHNAME', 'spherepoly_deg' - LANGUAGE 'c' - IMMUTABLE STRICT; - -COMMENT ON FUNCTION spoly_deg(float8[]) IS - ' Create spoly from array of points. - Two consecutive numbers among those present - refer to the same occurrence and cover its - latitude and longitude, respectively.'; - CREATE FUNCTION spoly_add_points_fin_aggr (spoly) RETURNS spoly AS 'MODULE_PATHNAME', 'spherepoly_add_points_finalize' diff --git a/sql/poly.sql b/sql/poly.sql index 2281aba..a3f36d4 100644 --- a/sql/poly.sql +++ b/sql/poly.sql @@ -78,6 +78,10 @@ SELECT spoly '{(359d,0d),(359d,1d),(4d,0d)}'; SELECT spoly '{(10d,0d),(10d,1d),(15d,0d)}'; +SELECT spoly(ARRAY[0.017453292519943295, 0.03490658503988659, 0.05235987755982988, 0.06981317007977318, 0.08726646259971647, 0.10471975511965977]); + +SELECT spoly(ARRAY[0.17453292519943295, 0.0, 0.17453292519943295, 0.017453292519943295, 0.2617993877991494, 0.0]); + SELECT spoly_deg(ARRAY[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]); SELECT spoly_deg(ARRAY[10.0, 0.0, 10.0, 1.0, 15.0, 0.0]); @@ -86,8 +90,18 @@ SELECT spoly_deg(ARRAY[10.0, 0.0, 10.0, 1.0, 15.0, 0.0]); SELECT spoly '{(10d,0d),(10d,1d)}'; +SELECT spoly(ARRAY[1.0, 2.0, 3.0, 4.0, 5.0]); + +SELECT spoly(ARRAY[1.0, 2.0, 3.0, NULL, 5.0, 6.0]); + +SELECT spoly(ARRAY[]::float8[]); + +SELECT spoly(NULL::float8[]); + SELECT spoly_deg(ARRAY[1.0, 2.0, 3.0, 4.0, 5.0]); +SELECT spoly_deg(ARRAY[1.0, 2.0, 3.0, NULL, 5.0, 6.0]); + SELECT spoly_deg(ARRAY[]::float8[]); SELECT spoly_deg(NULL::float8[]); diff --git a/src/polygon.c b/src/polygon.c index 57786b8..0ed6191 100644 --- a/src/polygon.c +++ b/src/polygon.c @@ -6,6 +6,7 @@ PG_FUNCTION_INFO_V1(spherepoly_in); PG_FUNCTION_INFO_V1(spherepoly_deg); +PG_FUNCTION_INFO_V1(spherepoly_rad); PG_FUNCTION_INFO_V1(spherepoly_equal); PG_FUNCTION_INFO_V1(spherepoly_equal_neg); PG_FUNCTION_INFO_V1(spherepoly_circ); @@ -906,6 +907,56 @@ spherepoly_in(PG_FUNCTION_ARGS) PG_RETURN_POINTER(poly); } +Datum +spherepoly_rad(PG_FUNCTION_ARGS) +{ + int i, + np; + ArrayType *float_vector = PG_GETARG_ARRAYTYPE_P(0); + float8 *array_data; + SPoint *points; + + np = ArrayGetNItems(ARR_NDIM(float_vector), ARR_DIMS(float_vector)); + + if (ARR_HASNULL(float_vector)) + { + elog(ERROR, + "spherepoly_rad: input array is invalid because it has null values" + ); + PG_RETURN_NULL(); + } + + if (np < 6 || np % 2 != 0) + { + elog(ERROR, + "spherepoly_rad: invalid number of arguments (must be even and >= 6)" + ); + PG_RETURN_NULL(); + } + + np /= 2; + + points = (SPoint *) palloc(np * sizeof(SPoint)); + if (points == NULL) + { + elog(ERROR, + "spherepoly_rad: failed to allocate memory for points array" + ); + PG_RETURN_NULL(); + } + + array_data = (float8 *) ARR_DATA_PTR(float_vector); + + for (i = 0; i < np; i++) + { + create_spherepoint_from_long_lat(&points[i], + array_data[2 * i], + array_data[2 * i + 1] + ); + } + PG_RETURN_POINTER(spherepoly_from_array(points, np)); +} + Datum spherepoly_deg(PG_FUNCTION_ARGS) { @@ -917,12 +968,19 @@ spherepoly_deg(PG_FUNCTION_ARGS) np = ArrayGetNItems(ARR_NDIM(float_vector), ARR_DIMS(float_vector)); + if (ARR_HASNULL(float_vector)) + { + elog(ERROR, + "spherepoly_deg: input array is invalid because it has null values" + ); + PG_RETURN_NULL(); + } + if (np < 6 || np % 2 != 0) { - elog( - ERROR, - "spherepoly_deg: invalid number of arguments (must be even and >= 6)" - ); + elog(ERROR, + "spherepoly_deg: invalid number of arguments (must be even and >= 6)" + ); PG_RETURN_NULL(); } @@ -931,10 +989,9 @@ spherepoly_deg(PG_FUNCTION_ARGS) points = (SPoint *) palloc(np * sizeof(SPoint)); if (points == NULL) { - elog( - ERROR, - "spherepoly_deg: failed for allocate memory for points array" - ); + elog(ERROR, + "spherepoly_deg: failed to allocate memory for points array" + ); PG_RETURN_NULL(); } @@ -942,11 +999,10 @@ spherepoly_deg(PG_FUNCTION_ARGS) for (i = 0; i < np; i++) { - create_spherepoint_from_long_lat( - &points[i], - deg_to_rad(array_data[2 * i]), - deg_to_rad(array_data[2 * i + 1]) - ); + create_spherepoint_from_long_lat(&points[i], + deg_to_rad(array_data[2 * i]), + deg_to_rad(array_data[2 * i + 1]) + ); } PG_RETURN_POINTER(spherepoly_from_array(points, np)); } diff --git a/src/polygon.h b/src/polygon.h index d52a9c6..e90b7c5 100644 --- a/src/polygon.h +++ b/src/polygon.h @@ -87,9 +87,14 @@ Datum spherepoly_get_point(PG_FUNCTION_ARGS); int8 poly_line_pos(const SPOLY *poly, const SLine *line); /* - * Input of a spherical from sequence of pairconsecutive numbers(lng, lat). + * Input of a spherical from array of pair-consecutive numbers (lng, lat), in radians. */ -Datum spherepoly_deg(PG_FUNCTION_ARGS); +Datum spherepoly_rad(PG_FUNCTION_ARGS); + +/* + * Input of a spherical from array of pair-consecutive numbers (lng, lat), in degrees. + */ +Datum spherepoly_deg(PG_FUNCTION_ARGS); /* * Input of a spherical polygon. diff --git a/upgrade_scripts/pg_sphere--1.3.1--1.3.2.sql.in b/upgrade_scripts/pg_sphere--1.3.1--1.3.2.sql.in index bdad0c2..3a80bc7 100644 --- a/upgrade_scripts/pg_sphere--1.3.1--1.3.2.sql.in +++ b/upgrade_scripts/pg_sphere--1.3.1--1.3.2.sql.in @@ -16,3 +16,20 @@ $$; -- remove legacy spellings of operators DROP OPERATOR IF EXISTS @(bigint, smoc); DROP OPERATOR IF EXISTS @(spoint, smoc); + +-- add spoly function that takes an array of float8 values in radians +CREATE FUNCTION spoly(float8[]) + RETURNS spoly + AS 'MODULE_PATHNAME', 'spherepoly_rad' + LANGUAGE 'c' + IMMUTABLE STRICT PARALLEL SAFE; + +COMMENT ON FUNCTION spoly(float8[]) IS + 'creates spoly from array of numbers in radians'; + +-- add PARALLEL SAFE to spoly_deg(float8[]) +ALTER FUNCTION spoly_deg(float8[]) IMMUTABLE STRICT PARALLEL SAFE; + +-- update comment on spoly_deg function +COMMENT ON FUNCTION spoly_deg(float8[]) IS + 'creates spoly from array of numbers in degrees';