Първо, съжалявам, ако наруша правила за публикуване тук, публикувам за първи път и всичко това.
Исках да използвам горния отговор заедно с добавката timescaledb към postgres за моята метеорологична станция „направи си сам“, но се оказа, че функцията не е паралелно безопасна. Също така afik използването на atan не дава правилния отговор.
Така че това е моята модифицирана версия, която според мен трябва да е паралелно безопасна и вместо това да използва atan2.
DROP AGGREGATE IF EXISTS vector_avg(float, float) CASCADE;
DROP TYPE IF EXISTS vector_sum CASCADE;
DROP TYPE IF EXISTS avg_vector CASCADE;
CREATE TYPE vector_sum AS (x float, y float, count int);
CREATE TYPE avg_vector AS (magnitude float, direction float);
CREATE OR REPLACE FUNCTION sum_vector (vectors vector_sum, magnitude float, direction float)
RETURNS vector_sum LANGUAGE sql PARALLEL SAFE STRICT AS
'SELECT vectors.x + (magnitude * cos(radians(direction))), vectors.y + (magnitude * sin(radians(direction))), vectors.count + 1';
CREATE OR REPLACE FUNCTION combine_sum (part1 vector_sum , part2 vector_sum)
RETURNS vector_sum LANGUAGE sql PARALLEL SAFE STRICT AS
'SELECT (part1.x+part2.x)/2,(part1.y+part2.y)/2,part1.count+part2.count';
CREATE OR REPLACE FUNCTION avg_vector_finalfunc(vectors vector_sum)
RETURNS avg_vector
AS
$$
DECLARE
x float;
y float;
d float;
BEGIN
BEGIN
IF vectors.count = 0 THEN
RETURN (NULL, NULL)::avg_vector;
END IF;
x := (vectors.x/vectors.count);
y := (vectors.y/vectors.count);
-- This means the vector is null vector
-- Please see: https://math.stackexchange.com/a/3682/10842
IF x = 0 OR y = 0 THEN
RETURN (0, 0)::avg_vector;
END IF;
d:=degrees(atan2(y,x));
-- atan2 returns negative result for angles > 180
IF d < 0 THEN
d := d+360;
END IF;
RETURN (sqrt(power(x, 2) + power(y, 2)), d )::avg_vector;
EXCEPTION WHEN others THEN
RETURN (NULL, NULL)::avg_vector;
END;
END;
$$
LANGUAGE 'plpgsql'
PARALLEL SAFE
RETURNS NULL ON NULL INPUT;
CREATE AGGREGATE vector_avg (float, float) (
sfunc = sum_vector
, stype = vector_sum
, combinefunc = combine_sum
, finalfunc = avg_vector_finalfunc
, initcond = '(0.0, 0.0, 0)'
, PARALLEL = SAFE
Тествайте от много малка извадка:
psql -d weather -c "select * from windavgtest;"
time | direction | speed
-------------------------------+-----------+-------
2019-08-01 16:51:53.199357+00 | 170 | 1
2019-08-01 16:51:54.388392+00 | 170 | 1
2019-08-01 16:51:55.335034+00 | 170 | 1
2019-08-01 16:51:56.362812+00 | 170 | 1
2019-08-01 16:52:07.191919+00 | 190 | 1
2019-08-01 16:52:08.250756+00 | 190 | 1
2019-08-01 16:52:09.193265+00 | 190 | 1
2019-08-01 16:52:10.224283+00 | 190 | 1
(8 rows)
дава:
psql -d weather -c "select round((vector_avg(speed, direction)).direction) AS wdirection from windavgtest;
"
wdirection
------------
180
(1 row)