Allereerst sorry als ik hier de regels voor het posten overtreed, de eerste keer een poster en zo.
Ik wilde het bovenstaande antwoord samen met de timescaledb-toevoeging aan postgres gebruiken voor mijn doe-het-zelf-weerstation, maar het blijkt dat de functie niet parallel veilig is. Ook het gebruik van atan levert niet het juiste antwoord op.
Dit is dus mijn aangepaste versie die volgens mij parallel veilig zou moeten zijn en in plaats daarvan atan2 gebruikt.
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
Test uit een zeer kleine steekproef:
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)
opbrengsten:
psql -d weather -c "select round((vector_avg(speed, direction)).direction) AS wdirection from windavgtest;
"
wdirection
------------
180
(1 row)