sql >> データベース >  >> RDS >> PostgreSQL

風速(ベクトルの大きさ)と風向(ベクトルの方向)のベクトル平均を計算するためのPostgres集計関数

    ここで投稿ルールに違反した場合は、最初に申し訳ありません。初めてのポスターなどです。

    私のDIY気象台のpostgresにtimescaledbを追加して上記の回答を使用したかったのですが、この関数は並列安全ではないことがわかりました。また、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)
    


    1. to_char()関数を使用せずにPostgreSQLで日付から年と月を抽出するにはどうすればよいですか?

    2. postgres:ユーザーをスーパーユーザーにアップグレードしますか?

    3. スプリングデータjpaを使用してjsonb列をクエリするにはどうすればよいですか?

    4. PostGIS:zとmの次元をクエリする(linestringzm)