From 118fe52b13cb13a170593830d4d769270cc016c2 Mon Sep 17 00:00:00 2001 From: stepan-neretin7 Date: Mon, 31 Jul 2023 17:22:11 +0700 Subject: [PATCH] [ISSUE #22] Created spoint_deg, scircle_deg functions. --- Makefile | 6 ++- expected/circle.out | 16 +++++++ expected/init_test.out.in | 22 ++++----- expected/init_test_healpix.out.in | 4 +- expected/points.out | 18 +++++++ expected/poly.out | 22 +++++++++ pgs_circle.sql.in | 8 ++++ pgs_point.sql.in | 9 ++++ pgs_polygon.sql.in | 12 +++++ pgs_types.sql.in | 1 - sql/circle.sql | 8 ++++ sql/points.sql | 6 +++ sql/poly.sql | 10 ++++ src/circle.c | 20 +++++++- src/circle.h | 5 ++ src/pgs_util.h | 5 ++ src/point.c | 28 +++++++++-- src/point.h | 10 ++++ src/polygon.c | 47 +++++++++++++++++++ src/polygon.h | 5 ++ .../pg_sphere--1.2.3--1.3.0.sql.in | 29 ++++++++++++ 21 files changed, 271 insertions(+), 20 deletions(-) create mode 100644 upgrade_scripts/pg_sphere--1.2.3--1.3.0.sql.in diff --git a/Makefile b/Makefile index dca3b7d..4e0f826 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,8 @@ DATA_built = $(RELEASE_SQL) \ pg_sphere--1.1.5beta4gavo--1.2.0.sql \ pg_sphere--1.2.0--1.2.1.sql \ pg_sphere--1.2.1--1.2.2.sql \ - pg_sphere--1.2.2--1.2.3.sql + pg_sphere--1.2.2--1.2.3.sql \ + pg_sphere--1.2.3--1.3.0.sql DOCS = README.pg_sphere COPYRIGHT.pg_sphere REGRESS = init tables points euler circle line ellipse poly path box index \ @@ -260,6 +261,9 @@ endif pg_sphere--1.2.2--1.2.3.sql: cat upgrade_scripts/$@.in > $@ +pg_sphere--1.2.3--1.3.0.sql: + cat upgrade_scripts/$@.in > $@ + # end of local stuff src/sscan.o : src/sparse.c diff --git a/expected/circle.out b/expected/circle.out index 60a0159..02e5334 100644 --- a/expected/circle.out +++ b/expected/circle.out @@ -5,6 +5,22 @@ (1 row) -- Input/Output --- +SELECT scircle_deg(spoint(10,10), 90); + scircle_deg +------------------------------------------ + <(0.57522204 , -0.57522204) , 1.5707963> +(1 row) + +SELECT scircle_deg(spoint(10,10), 91); +ERROR: radius must not be greater than 90 degrees or less than 0 +SELECT scircle_deg(spoint(0,0), 0); + scircle_deg +--------------- + <(0 , 0) , 0> +(1 row) + +SELECT scircle_deg(spoint(10,10), -1); +ERROR: radius must not be greater than 90 degrees or less than 0 SELECT set_sphere_output( 'RAD' ); set_sphere_output ------------------- diff --git a/expected/init_test.out.in b/expected/init_test.out.in index 129283c..4b0671c 100644 --- a/expected/init_test.out.in +++ b/expected/init_test.out.in @@ -18,18 +18,18 @@ psql:pg_sphere.test.sql:108: NOTICE: argument type sellipse is only a shell psql:pg_sphere.test.sql:126: NOTICE: type "spoly" is not yet defined DETAIL: Creating a shell type definition. psql:pg_sphere.test.sql:133: NOTICE: argument type spoly is only a shell -psql:pg_sphere.test.sql:152: NOTICE: type "spath" is not yet defined +psql:pg_sphere.test.sql:151: NOTICE: type "spath" is not yet defined DETAIL: Creating a shell type definition. -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 +psql:pg_sphere.test.sql:158: NOTICE: argument type spath is only a shell +psql:pg_sphere.test.sql:177: 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:184: NOTICE: argument type sbox is only a shell +psql:pg_sphere.test.sql:8568: 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:8575: NOTICE: argument type spherekey is only a shell +psql:pg_sphere.test.sql:8589: 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:8596: NOTICE: argument type pointkey is only a shell +psql:pg_sphere.test.sql:8602: NOTICE: argument type pointkey is only a shell +psql:pg_sphere.test.sql:8608: NOTICE: argument type pointkey is only a shell +psql:pg_sphere.test.sql:8614: 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..7e725f8 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:9181: NOTICE: return type smoc is only a shell +psql:pg_sphere.test.sql:9187: NOTICE: argument type smoc is only a shell diff --git a/expected/points.out b/expected/points.out index 84375e6..6fb3c17 100644 --- a/expected/points.out +++ b/expected/points.out @@ -239,6 +239,24 @@ SELECT spoint(0.0109083078249646 , -0.000727220521664407); (0.625d , -0.041666667d) (1 row) +SELECT spoint_deg(57.2958, 57.2958); + spoint_deg +----------------------- + (57.2958d , 57.2958d) +(1 row) + +SELECT spoint_deg(0, 0); + spoint_deg +------------ + (0d , 0d) +(1 row) + +SELECT spoint_deg(114.5916, 0); + spoint_deg +------------------ + (114.5916d , 0d) +(1 row) + SELECT set_sphere_output( 'RAD' ); set_sphere_output ------------------- diff --git a/expected/poly.out b/expected/poly.out index 201b9be..a705483 100644 --- a/expected/poly.out +++ b/expected/poly.out @@ -318,11 +318,33 @@ SELECT spoly '{(10d,0d),(10d,1d),(15d,0d)}'; {(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 +--------------------------------- + {(1d , 2d),(3d , 4d),(5d , 6d)} +(1 row) + +SELECT spoly_deg(ARRAY[10.0, 0.0, 10.0, 1.0, 15.0, 0.0]); + spoly_deg +------------------------------------ + {(10d , 0d),(10d , 1d),(15d , 0d)} +(1 row) + -- incorrect input ----- SELECT spoly '{(10d,0d),(10d,1d)}'; ERROR: spherepoly_in: more than two points needed LINE 1: SELECT spoly '{(10d,0d),(10d,1d)}'; ^ +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[]::float8[]); +ERROR: spherepoly_deg: invalid number of arguments (must be even and >= 6) +SELECT spoly_deg(NULL::float8[]); + spoly_deg +----------- + +(1 row) + --- self-crossing input ----- SELECT spoly '{(0d,0d),(10d,10d),(0d,10d),(10d,0d)}'; ERROR: spherepoly_from_array: a line segment overlaps or polygon too large diff --git a/pgs_circle.sql.in b/pgs_circle.sql.in index e0c110d..9e386dd 100644 --- a/pgs_circle.sql.in +++ b/pgs_circle.sql.in @@ -32,6 +32,14 @@ CREATE FUNCTION scircle(spoint, float8) COMMENT ON FUNCTION scircle(spoint, float8) IS 'spherical circle with spherical point as center and float8 as radius in radians'; +CREATE FUNCTION scircle_deg(spoint, float8) + RETURNS scircle + AS 'MODULE_PATHNAME' , 'spherecircle_by_center_deg' + LANGUAGE 'c' + IMMUTABLE STRICT PARALLEL SAFE; + +COMMENT ON FUNCTION scircle_deg(spoint, float8) IS + 'spherical circle with spherical point as center and float8 as radius in degrees'; -- -- Casting point as circle diff --git a/pgs_point.sql.in b/pgs_point.sql.in index a9afb84..908707e 100644 --- a/pgs_point.sql.in +++ b/pgs_point.sql.in @@ -15,6 +15,12 @@ CREATE FUNCTION spoint(FLOAT8, FLOAT8) LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE; +CREATE FUNCTION spoint_deg(FLOAT8, FLOAT8) + RETURNS spoint + AS 'MODULE_PATHNAME', 'spherepoint_from_long_lat_deg' + LANGUAGE 'c' + IMMUTABLE STRICT PARALLEL SAFE; + CREATE FUNCTION set_sphere_output_precision(INT4) RETURNS CSTRING AS 'MODULE_PATHNAME', 'set_sphere_output_precision' @@ -28,6 +34,9 @@ CREATE FUNCTION set_sphere_output(CSTRING) COMMENT ON FUNCTION spoint(FLOAT8, FLOAT8) IS 'returns a spherical point from longitude (arg1), latitude (arg2)'; +COMMENT ON FUNCTION spoint_deg(FLOAT8, FLOAT8) IS + 'returns a spherical point from longitude (arg1, in degrees), latitude (arg2, in degrees)'; + CREATE FUNCTION long(spoint) RETURNS FLOAT8 AS 'MODULE_PATHNAME', 'spherepoint_long' diff --git a/pgs_polygon.sql.in b/pgs_polygon.sql.in index 701c860..df5a614 100644 --- a/pgs_polygon.sql.in +++ b/pgs_polygon.sql.in @@ -928,6 +928,18 @@ 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/pgs_types.sql.in b/pgs_types.sql.in index e0ff8f3..4bede83 100644 --- a/pgs_types.sql.in +++ b/pgs_types.sql.in @@ -132,7 +132,6 @@ CREATE FUNCTION spoly_out(spoly) LANGUAGE 'c' IMMUTABLE STRICT; - CREATE TYPE spoly ( internallength = VARIABLE, input = spoly_in, diff --git a/sql/circle.sql b/sql/circle.sql index 5fbbc91..0a57cab 100644 --- a/sql/circle.sql +++ b/sql/circle.sql @@ -5,6 +5,14 @@ SET extra_float_digits = 0; -- Input/Output --- +SELECT scircle_deg(spoint(10,10), 90); + +SELECT scircle_deg(spoint(10,10), 91); + +SELECT scircle_deg(spoint(0,0), 0); + +SELECT scircle_deg(spoint(10,10), -1); + SELECT set_sphere_output( 'RAD' ); SELECT '< (1h 2m 30s , +1d 2m 30s), 1.0d >'::scircle; diff --git a/sql/points.sql b/sql/points.sql index a2d53a6..5e63260 100644 --- a/sql/points.sql +++ b/sql/points.sql @@ -86,6 +86,12 @@ SELECT '(0.0109083078249646 , -0.000727220521664407)'::spoint; SELECT spoint(0.0109083078249646 , -0.000727220521664407); +SELECT spoint_deg(57.2958, 57.2958); + +SELECT spoint_deg(0, 0); + +SELECT spoint_deg(114.5916, 0); + SELECT set_sphere_output( 'RAD' ); SELECT spoint(7.28318530717958623 , 0.00); diff --git a/sql/poly.sql b/sql/poly.sql index 4168024..52cedd7 100644 --- a/sql/poly.sql +++ b/sql/poly.sql @@ -78,10 +78,20 @@ SELECT spoly '{(359d,0d),(359d,1d),(4d,0d)}'; SELECT spoly '{(10d,0d),(10d,1d),(15d,0d)}'; +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]); + -- incorrect input ----- SELECT spoly '{(10d,0d),(10d,1d)}'; +SELECT spoly_deg(ARRAY[1.0, 2.0, 3.0, 4.0, 5.0]); + +SELECT spoly_deg(ARRAY[]::float8[]); + +SELECT spoly_deg(NULL::float8[]); + --- self-crossing input ----- SELECT spoly '{(0d,0d),(10d,10d),(0d,10d),(10d,0d)}'; diff --git a/src/circle.c b/src/circle.c index f2b2992..e28fe0c 100644 --- a/src/circle.c +++ b/src/circle.c @@ -1,4 +1,5 @@ #include "circle.h" +#include "pgs_util.h" /* Circle functions */ @@ -22,6 +23,7 @@ PG_FUNCTION_INFO_V1(spherecircle_center); PG_FUNCTION_INFO_V1(spherecircle_radius); PG_FUNCTION_INFO_V1(spherepoint_to_circle); PG_FUNCTION_INFO_V1(spherecircle_by_center); +PG_FUNCTION_INFO_V1(spherecircle_by_center_deg); PG_FUNCTION_INFO_V1(spherecircle_area); PG_FUNCTION_INFO_V1(spherecircle_circ); PG_FUNCTION_INFO_V1(spheretrans_circle); @@ -79,7 +81,7 @@ spherecircle_in(PG_FUNCTION_ARGS) { pfree(c); c = NULL; - elog(ERROR, "spherecircle_in: radius must be not greater than 90 degrees"); + elog(ERROR, "spherecircle_in: radius must not be greater than 90 degrees or less than 0"); } else if (FPeq(c->radius, PIH)) { @@ -361,7 +363,7 @@ spherecircle_by_center(PG_FUNCTION_ARGS) if (FPgt(rad, PIH) || FPlt(rad, 0.0)) { - elog(ERROR, "radius must be not greater than 90 degrees or less than 0"); + elog(ERROR, "radius must not be greater than 90 degrees or less than 0"); PG_RETURN_NULL(); } c = (SCIRCLE *) palloc(sizeof(SCIRCLE)); @@ -370,6 +372,20 @@ spherecircle_by_center(PG_FUNCTION_ARGS) PG_RETURN_POINTER(c); } +Datum +spherecircle_by_center_deg(PG_FUNCTION_ARGS) +{ + Datum res; + SPoint *p = (SPoint *) PG_GETARG_POINTER(0); + const float8 rad = deg_to_rad(PG_GETARG_FLOAT8(1)); + res = DirectFunctionCall2( + spherecircle_by_center, + PointerGetDatum(p), + Float8GetDatum(rad) + ); + PG_RETURN_DATUM(res); +} + Datum spherecircle_area(PG_FUNCTION_ARGS) { diff --git a/src/circle.h b/src/circle.h index 8ac5175..b8aa520 100644 --- a/src/circle.h +++ b/src/circle.h @@ -132,6 +132,11 @@ Datum spherepoint_to_circle(PG_FUNCTION_ARGS); */ Datum spherecircle_by_center(PG_FUNCTION_ARGS); +/* + * Creates a circle from center and radius(in degrees). + */ +Datum spherecircle_by_center_deg(PG_FUNCTION_ARGS); + /* * Calculates the area of a circle in square radians. */ diff --git a/src/pgs_util.h b/src/pgs_util.h index ad9763b..b79170b 100644 --- a/src/pgs_util.h +++ b/src/pgs_util.h @@ -26,4 +26,9 @@ conv_theta(double x) return y; } +static inline double deg_to_rad(double in) +{ + return in * PI / 180; +} + #endif diff --git a/src/point.c b/src/point.c index 5132835..c68ee71 100644 --- a/src/point.c +++ b/src/point.c @@ -1,9 +1,11 @@ #include "point.h" +#include "pgs_util.h" /* This file contains definitions for spherical point functions. */ PG_FUNCTION_INFO_V1(spherepoint_in); PG_FUNCTION_INFO_V1(spherepoint_from_long_lat); +PG_FUNCTION_INFO_V1(spherepoint_from_long_lat_deg); PG_FUNCTION_INFO_V1(spherepoint_distance); PG_FUNCTION_INFO_V1(spherepoint_long); PG_FUNCTION_INFO_V1(spherepoint_lat); @@ -141,18 +143,38 @@ spherepoint_in(PG_FUNCTION_ARGS) PG_RETURN_POINTER(sp); } +void create_spherepoint_from_long_lat(SPoint *p, float8 lng, float8 lat) +{ + p->lat = lat; + p->lng = lng; + spoint_check(p); +} Datum spherepoint_from_long_lat(PG_FUNCTION_ARGS) { SPoint *p = (SPoint *) palloc(sizeof(SPoint)); - p->lng = PG_GETARG_FLOAT8(0); - p->lat = PG_GETARG_FLOAT8(1); - spoint_check(p); + const float8 lng = PG_GETARG_FLOAT8(0); + const float8 lat = PG_GETARG_FLOAT8(1); + create_spherepoint_from_long_lat(p, lng, lat); PG_RETURN_POINTER(p); } +Datum +spherepoint_from_long_lat_deg(PG_FUNCTION_ARGS) +{ + Datum res; + const float8 lng = deg_to_rad(PG_GETARG_FLOAT8(0)); + const float8 lat = deg_to_rad(PG_GETARG_FLOAT8(1)); + res = DirectFunctionCall2( + spherepoint_from_long_lat, + Float8GetDatum(lng), + Float8GetDatum(lat) + ); + PG_RETURN_DATUM(res); +} + static double norm2(double a, double b) { diff --git a/src/point.h b/src/point.h index 76c2202..89197d0 100644 --- a/src/point.h +++ b/src/point.h @@ -45,11 +45,21 @@ void spoint_vector3d(Vector3D *v, const SPoint *p); */ Datum spherepoint_in(PG_FUNCTION_ARGS); +/* + * Create spherical point from lat, lng and store to first argument(pointer) + */ +void create_spherepoint_from_long_lat(SPoint *p, float8 lng, float8 lat); + /* * Create a spherical point from longitude and latitude both in radians. */ Datum spherepoint_from_long_lat(PG_FUNCTION_ARGS); +/* + * Create a spherical point from longitude and latitude both in degrees. + */ +Datum spherepoint_from_long_lat_deg(PG_FUNCTION_ARGS); + /* * Calculate the distance between two spherical points. */ diff --git a/src/polygon.c b/src/polygon.c index 8ae3fe8..17f4841 100644 --- a/src/polygon.c +++ b/src/polygon.c @@ -1,9 +1,11 @@ #include "polygon.h" +#include "point.h" /* Polygon functions */ PG_FUNCTION_INFO_V1(spherepoly_in); +PG_FUNCTION_INFO_V1(spherepoly_deg); PG_FUNCTION_INFO_V1(spherepoly_equal); PG_FUNCTION_INFO_V1(spherepoly_equal_neg); PG_FUNCTION_INFO_V1(spherepoly_circ); @@ -850,6 +852,51 @@ spherepoly_in(PG_FUNCTION_ARGS) PG_RETURN_POINTER(poly); } +Datum +spherepoly_deg(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 (np < 6 || np % 2 != 0) + { + elog( + ERROR, + "spherepoly_deg: 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_deg: failed for 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], + deg_to_rad(array_data[2 * i]), + deg_to_rad(array_data[2 * i + 1]) + ); + } + PG_RETURN_POINTER(spherepoly_from_array(points, np)); +} + Datum spherepoly_equal(PG_FUNCTION_ARGS) { diff --git a/src/polygon.h b/src/polygon.h index 7f2aa80..eefd099 100644 --- a/src/polygon.h +++ b/src/polygon.h @@ -81,6 +81,11 @@ bool spoly_contains_point(const SPOLY *pg, const SPoint *sp); */ int8 poly_line_pos(const SPOLY *poly, const SLine *line); +/* + * Input of a spherical from sequence of pairconsecutive numbers(lng, lat). + */ +Datum spherepoly_deg(PG_FUNCTION_ARGS); + /* * Input of a spherical polygon. */ diff --git a/upgrade_scripts/pg_sphere--1.2.3--1.3.0.sql.in b/upgrade_scripts/pg_sphere--1.2.3--1.3.0.sql.in new file mode 100644 index 0000000..86cc233 --- /dev/null +++ b/upgrade_scripts/pg_sphere--1.2.3--1.3.0.sql.in @@ -0,0 +1,29 @@ +CREATE FUNCTION scircle_deg(spoint, float8) + RETURNS scircle + AS 'MODULE_PATHNAME' , 'spherecircle_by_center_deg' + LANGUAGE 'c' + IMMUTABLE STRICT PARALLEL SAFE; + +COMMENT ON FUNCTION scircle_deg(spoint, float8) IS + 'spherical circle with spherical point as center and float8 as radius in degrees'; + +CREATE FUNCTION spoint_deg(FLOAT8, FLOAT8) + RETURNS spoint + AS 'MODULE_PATHNAME', 'spherepoint_from_long_lat_deg' + LANGUAGE 'c' + IMMUTABLE STRICT PARALLEL SAFE; + +COMMENT ON FUNCTION spoint_deg(FLOAT8, FLOAT8) IS + 'returns a spherical point from longitude (arg1, in degrees), latitude (arg2, in degrees)'; + +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.';