2024,江端さんの技術メモ

C言語を使えて、GStreamerが使えて、GO言語を使えて、CGOが使える環境を作っています。

(1)Windowsの方のmsys2とGStreamerのパッケージを削除
Windowsの方のC言語、GStreamerはコンフリクトするので削除しますが、GO言語は、別のタスク(大学の方のシミュレータ)があるので、削除できませんでした。
という訳で、MSYS2の環境を、Windowsの方と切り離して運用するという覚悟ができました。

(2)C:の容量が小さいので、G:\の方にパッケージを分離していたのですが、これをやると、全く動かなくなるので、C:\msys2の配下に勝手に作成される、/c/msys64/home/tomoi/の直下でプログラムの作成をすることにしました。

これ、/usr/local/binの配下に/home/ebataを作るような気持ち悪さがあるのですが、今回は稼動が最優先です。

MSYS2のインストール

  1. MSYS2公式サイトからインストーラをダウンロードします。
  2. インストーラを実行し、画面の指示に従ってMSYS2をインストールします。

2. MSYS2の初期設定

  1. MSYS2ターミナルを起動: MSYS2 MSYSショートカットを使用してMSYS2ターミナルを起動します。
  2. パッケージデータベースと基本パッケージの更新: 以下のコマンドを順番に実行して、パッケージデータベースと基本パッケージを更新します。

    sh

    pacman -Syu

    初回のアップデートが完了したら、ターミナルを一度閉じて再起動します。

  3. 再度更新を実行: ターミナルを再起動後、再度以下のコマンドを実行します。

    sh

    pacman -Syu

3. MinGW-w64のインストール

  1. MinGW-w64ツールチェインのインストール: MSYS2 MinGW 64-bitショートカットを使用してMSYS2ターミナルを起動し、以下のコマンドを実行します。

    sh

    pacman -S mingw-w64-x86_64-toolchain

    これにより、GCC、G++、およびその他の開発ツールがインストールされます。

4. 環境変数の設定

MinGW-w64のバイナリにアクセスするために、環境変数PATHを設定します。

  1. ~/.bashrcファイルの編集: MSYS2ターミナルで~/.bashrcファイルを開きます。

    sh

    nano ~/.bashrc
  2. PATHの設定: 以下の行を追加して保存します。

    sh

    export PATH="/mingw64/bin:$PATH"
  3. 設定の反映: 設定を反映させるために、ターミナルを再起動するか、以下のコマンドを実行します。

    sh

    source ~/.bashrc

5. GStreamerのインストール

  1. GStreamerのパッケージのインストール: MSYS2 MinGW 64-bitショートカットを使用してMSYS2ターミナルを起動し、以下のコマンドを実行します。

    sh

    pacman -S mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base mingw-w64-x86_64-gst-plugins-good mingw-w64-x86_64-gst-plugins-bad mingw-w64-x86_64-gst-plugins-ugly

    pacman -S mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base mingw-w64-x86_64-gst-plugins-good mingw-w64-x86_64-gst-plugins-bad mingw-w64-x86_64-gst-libav

6. 確認とテスト

  1. GCCの動作確認: GCCが正しくインストールされているか確認します。

    sh

    gcc --version
  2. 簡単なCプログラムの作成とコンパイル: 簡単なCプログラムを作成してコンパイルします。

    sh

    nano test.c

    以下の内容を入力します。

    c

    #include <stdio.h>

    int main() {
    printf("Hello, World!\n");
    return 0;
    }

    ファイルを保存してNanoエディタを閉じた後、コンパイルします。

    sh

    gcc test.c -o test.exe

    コンパイルが成功したら、実行します。

    sh

    ./test.exe
  3. GStreamerの動作確認: GStreamerが正しくインストールされているか確認します。

    sh

    gst-launch-1.0 --version

    簡単なGStreamerパイプラインを実行して動作確認します。

    sh

    gst-launch-1.0 videotestsrc ! autovideosink

とうぜん 、ここまですんなりできた訳ではありませんが、取り敢えず私が思い出せる程度の情報を載せてあります。

-----

https://github.com/go-gst/go-gst

から、/home/tomoi/go/srcで、 git clone https://github.com/go-gst/go-gst をして、/home/tomoi/go/src/go-gst を作って、

に張ってあったプログラムを、main.goでセーブしたのち、

go run main.go videotestsrc ! glimagesink

で、

と、

が、表われることを確認。

なにがどうなっているのかは、サッパリ分からないが、とりあえず、GoからGstreamerが動いたことは確認できた。

以上

なんか、サラっと書くと自分でも腹たつなぁ。10時間以上はかかったんだけどなぁ

2024,江端さんの技術メモ

Windows10に、GStreamerの1.24をインストール(フルインストールを選ぶこと)をしたのですが、定番の、

$ gst-launch-1.0.exe videotestsrc ! autovideosink
Use Windows high-resolution clock, precision: 1 ms
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
Got context from element 'autovideosink0': gst.d3d11.device.handle=context, device=(GstD3D11Device)"
\(GstD3D11Device\)\ d3d11device2", adapter=(uint)0, adapter-luid=(gint64)41376, device-id=(uint)1042
, vendor-id=(uint)32902, hardware=(boolean)true, description=(string)"Intel\(R\)\ HD\ Graphics\ 4600
";
ERROR: from element /GstPipeline:pipeline0/GstAutoVideoSink:autovideosink0/GstD3D11VideoSink:autovid
eosink0-actual-sink-d3d11video: Cannot create converter
Additional debug info:
Failed to prepare d3d11window
ERROR: pipeline doesn't want to preroll.
Setting pipeline to NULL ...
ERROR: from element /GstPipeline:pipeline0/GstVideoTestSrc:videotestsrc0: Internal data stream error
.
Additional debug info:
../libs/gst/base/gstbasesrc.c(3177): gst_base_src_loop (): /GstPipeline:pipeline0/GstVideoTestSrc:vi
deotestsrc0:
streaming stopped, reason error (-5)
ERROR: pipeline doesn't want to preroll.
Freeing pipeline ...

となります。別のWindows BOXでも、先日も経験して、青ざめていました。

結果として、

$ gst-launch-1.0 videotestsrc ! glimagesink

とすれば動きます。

autovideosinkが動かん、っていうのは、結局分かっていませんが、これは困りものです。

以下自分用メモ

-UDP通信の実験

-受信側

-gst-launch-1.0 videotestsrc ! x264enc ! rtph264pay ! udpsink host=192.168.101.10 port=38089
(ローカルの場合、host=127.0.0.1とする.  host=localhost は動かない)

-送信側

-gst-launch-1.0 udpsrc port=38089 caps="application/x-rtp,media=video,encoding-name=H264,payload=96" ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! glimagesink

-

-SRT通信の実験

-受信側

-gst-launch-1.0 srtsrc uri="srt://0.0.0.0:38089?mode=listener" keep-listening=true ! tsdemux ! h264parse ! avdec_h264 ! videoconvert ! glimagesink sync=false

-送信側

-gst-launch-1.0 rtspsrc location=rtsp://cam:Cam12345@192.168.0.10/Src/MediaInput/stream_1 latency=0 ! rtph264depay ! avdec_h264 ! videoconvert ! videoscale ! video/x-raw,width=640,height=360 ! videorate ! video/x-raw,framerate=5/1 ! x264enc speed-preset=ultrafast tune=zerolatency key-int-max=1 ! mpegtsmux ! srtserversink uri = \"srt://192.168.101.10:38089\" latency=500

2022/12,江端さんの技術メモ

自分の環境で作ったpostgresqlのDBを、先方にわざわざ作ってもらうのは申し訳ないので、丸ごとコピーして送付する方法

環境はこんな感じ

[192.168.0.23 の中に作ったDockerのPostgresqlのDBを起動]

中身は、

C:\Users\ebata>psql -U postgres -h 192.168.0.23 -p 15432
Password for user postgres:
psql (13.4, server 12.5 (Debian 12.5-1.pgdg100+1))
Type "help" for help.
postgres=# \l
                                 List of databases
   Name    |  Owner   | Encoding |  Collate   |   Ctype    |   Access privileges
-----------+----------+----------+------------+------------+-----------------------
 agent_db  | postgres | UTF8     | en_US.utf8 | en_US.utf8 |
 kitaya_db | postgres | UTF8     | en_US.utf8 | en_US.utf8 |
 postgres  | postgres | UTF8     | en_US.utf8 | en_US.utf8 |
 template0 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
           |          |          |            |            | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
           |          |          |            |            | postgres=CTc/postgres
(5 rows)

[192.168.0.8(localhost)に入っている psqlクライアント一式]

【PostgreSQL】Windows に psql コマンドだけをインストールする手順

で、

C:\Users\ebata>pg_dump -U postgres -h 192.168.0.23 -p 15432 kitaya_db > kitaya_db.sql
Password:

C:\Users\ebata>pg_dump -U postgres -h 192.168.0.23 -p 15432 agent_db > agent_db.sql
Password:

で完了

あとは、kitaya_db.sql と agent_db.sqlを先方に送って、

バスが宇都宮ライトレールの上を驀進している ―― 宇都宮ライトレールの利用を拒否させるような、ダイクストラをどうやって作ろうか

の最後の2行10行を参考に、DBを作って貰えばOK

以上

2024,江端さんの技術メモ

dockerは、どのOS環境でも動くを唄っていますが、基本的に、わたしは、どんなパッケージであろうとも、「これ」を信じていません。

とくに時系列方向については、全く信用していません。

ともあれ、ノートPCにtomioka_dbをdocker-compose.ymlで再構築した時、以下の部分の変更が必要でしたので、逐次メモしていきます。

#version: '3.9'
services:
  db:
    build:
      context: .
    environment:
      POSTGRES_PASSWORD: password
      POSTGRES_DB: tomioka_db
    ports:
      - "15432:5432"
    volumes:
      - ./tomioka_db:/tomioka_db
      - db-data:/var/lib/postgresql/data
  osm2pgsql:
    # image: openfirmware/osm2pgsql:latest
    image: osmtw/osm2pgsql:latest  # イメージ名を変更
    environment:
      PG_PORT_5432_TCP_ADDR: db
      PG_PORT_5432_TCP_PORT: 5432
      PG_ENV_OSM_DB: tomioka_db
      PG_ENV_OSM_USER: postgres
      PG_ENV_POSTGRES_PASSWORD: password
    volumes:
      - ./tomioka_db:/tomioka_db
volumes:
  db-data:

 

2024,江端さんの忘備録

私は今でも、Meadow3を使っています。

I still use Meadow3.

Meadow3とは、Windowsに特化したemacsのことです。

Meadow3 is a Windows-specific emacs.

WindowsNT 4.0より前から使っていますが、今も使い続けています。

I have been using it since before Windows NT 4.0 and continue to use it.

なぜか ―― 動くからです。

Why -- because it works.

現在、Windows11上でも動き続けています。

Currently, it continues to run on Windows 11.

vscodeのemacsキーバインドマッピング 諦めました。

-----

動き続けている限り、使わない理由はありません。

There is no reason not to use it as long as it keeps moving.

.emacsをメンテナンスする気力もありませんので、キーバインドも、当時から固定のままです。

I don't have the energy to maintain .emacs, so key bindings have remained fixed since that time.

もはやMeadow3は、私の体の一部といっても過言ではありません。

It is no exaggeration to say that Meadow3 is now a part of my body.

-----

Meadowが、バージョンアップしたWindowsOSで動かなくなった時に、私の中でも何かが終わるような気がしています。

I feel that something will end for me when Meadow stops working with the upgraded Windows OS.

―― と言いながら、次女のPCにMeadow3をインストールして、今、それで、この文章を書いています。

-- but I installed Meadow3 on my second daughter's PC and am now writing this text with it.

あと、次女のノートPCに大量に貼ってあるステッカーが、ちょっとアバンギャルドで、このPCを学会会場に持ち込む勇気が出ませんでした。

Also, the large number of stickers on my second daughter's laptop was a bit avant-garde, and I could not find the courage to bring this computer to the conference venue.

という訳で、こんなの購入して、ノートPCに貼りつけていました。

So I bought one like this and stuck it on my laptop.

返却時に、元に戻して返すつもりですが。

I intend to return it to its original state when I return it.

2024,江端さんの技術メモ

gnuplotで
0.98, 0.40, 0.30
0.98, 0.41, 0.30
0.97, 0.42, 0.30
0.96, 0.42, 0.30
0.98, 0.42, 0.31
0.98, 0.41, 0.31
のようにx,y,z座標で記載された、cluster1.csvの内容を、Gnuplotで表示するコマンドを教えて下さい。

ChatGPT
ChatGPT
以下は、cluster1.csv の内容を Gnuplot で表示するためのコマンドです。まず、CSVファイルの内容を正しく読み込むためのスクリプトを作成し、その後に3Dプロットを行います。

cluster1.csv の内容が次のようになっていると仮定します:
コードをコピーする
0.98, 0.40, 0.30
0.98, 0.41, 0.30
0.97, 0.42, 0.30
0.96, 0.42, 0.30
0.98, 0.42, 0.31
0.98, 0.41, 0.31
Gnuplot を使用してこのデータをプロットするためのスクリプトを作成します。以下はそのスクリプトです:
gnuplot
コードをコピーする
set datafile separator ","
set title "Cluster 1 Data"
set xlabel "X-axis"
set ylabel "Y-axis"
set zlabel "Z-axis"
set xrange [0:1]
set yrange [0:1]
set zrange [0:1]
set ticslevel 0
splot 'cluster1.csv' using 1:2:3 with points pt 7 ps 1 lc rgb "blue" title 'Cluster 1'

複数個出すのであれば、こんな感じ
splot 'cluster1.csv' using 1:2:3 with points pt 7 ps 1 lc rgb "blue" title 'Cluster 1', 'cluster2.csv' using 1:2:3 with points pt 7 ps 1 lc rgb "red" title 'Cluster 2', 'cluster3.csv' using 1:2:3 with points pt 7 ps 1 lc rgb "yellow" title 'Cluster 3'

splot 'cluster1.csv' using 1:2:3 with points pt 7 ps 2 lc rgb "blue" title 'Cluster 1', 'cluster2.csv' using 1:2:3 with points pt 7 ps 2 lc rgb "red" title 'Cluster 2', 'cluster3.csv' using 1:2:3 with points pt 7 ps 2 lc rgb "yellow" title 'Cluster 3', 'cluster4.csv' using 1:2:3 with points pt 7 ps 2 lc rgb "green" title 'Cluster 4', 'cluster5.csv' using 1:2:3 with points pt 7 ps 2 lc rgb "cyan" title 'Cluster 5', 'cluster6.csv' using 1:2:3 with points pt 7 ps 2 lc rgb "magenta" title 'Cluster 6', 'cluster7.csv' using 1:2:3 with points pt 7 ps 2 lc rgb "orange" title 'Cluster 7'

このスクリプトを Gnuplot に読み込むことで、3Dプロットが作成されます。具体的な手順は以下の通りです。

Gnuplot を起動します。
上記のスクリプトをコマンドラインに入力するか、ファイルに保存して load コマンドで読み込みます。
例えば、スクリプトを plot_cluster1.gnuplot というファイルに保存した場合:

sh
コードをコピーする
gnuplot> load 'plot_cluster1.gnuplot'
これで、cluster1.csv の内容が3Dプロットとして表示されます。プロットのポイントの形状や色、サイズは必要に応じて調整できます。

2023,江端さんの技術メモ

DockerのSQL-DBが死んだら、と、考えるだけで恐怖なので、 8TBのHDDの方にDockerの構築環境まるごとコピーして、そこに、Dockerのイメージも放り込んでおくことにした。

Step 1 Docker Desktopを立ち上げて、"tomioka"をアクティベートにする

Step 2 "docker ps" で起動状態を確認する

Step 3 "$ docker commit tomioka-db-1 tomioka_db_backup"をする
ちなみに実施するディレクトリはどこでもいい。tomioka_db_backupはファイルではないので。

Step.4 "$ docker images"をして、作成を確認する

イメージができているのを確認する。

Step.5 C:\の直下に、DockerBackupsという名前のディレクトリはすでに掘られているので、

Step.6 Command.com(他のシェルではダメみたい)"$ docker save -o C:\DockerBackups\tomioka_db_backup.tar tomioka_db_backup" を実施する

で、これを、F:\backup\tomoi\tomiokaに移動しておく。
こっちで纏めて管理しておく。

で、同じことを、person_trip1(×person_trip)でも行う(以下省略)

 

=======

 

私、DockerでDBの大量生産をやっているのですが、このDockerを起動させるPCが不具合のような兆候を見せて、今年最大の「真っ青」な状態になりました。

2T程度のHDDを購入して、Dockerコンテナのバックアップしておかなければ、ここ1年の研究が吹き飛ぶと実感しました。

とりあえず、逃し方のメモを取っておきます。

Step 1 Docker Desktopを立ち上げて、"kitaya"をアクティベートにする

Step 2 "docker ps" で起動状態を確認する

Step 3 "$ docker commit kitaya-db-1 kitaya_db_backup"をする
ちなみに実施するディレクトリはどこでもいい。kitaya_db_backupはファイルではないので。

Step.4 "$ docker images"をして、作成を確認する

イメージができているのを確認する。

Step.5 C:\の直下に、DockerBackupsという名前のディレクトリを掘っておく。

Step.6 "$ docker save -o C:\DockerBackups\kitaya_db_backup.tar kitaya_db_backup" を実施する

というファイルができる。

Step.7 "docker rmi kitaya-db-1_backup" でイメージを消去する(しなくてもいいが)。無駄なリソースを削除するためである。

ちなみに、kitaya_db_backup.tarも、サイズがバカでかいので、外部HDDなどに逃しておく方が良いと思う。

 


ちなみに、(試していないが)リストアはこんな感じになる(らしい)

docker load -i C:\DockerBackups\kitaya_backup.tar


ところで、Amazonで購入した2TのHDDが、まるで使えんので、返品することになりました。

この"評価1"て何だ?

私は、これを注文したハズなんだが? (だれが評価1、評価者2の商品なんぞ買うか)

なんか、やられたのかなぁ。腹立つなぁ。返品できるだけマシだが。

2024,江端さんの技術メモ

User
You
type LocInfo struct { Lon float64 Lat float64 Source int } という型があり、 route, dis := getDijkstraPath(db_agent_od, LocInfo{Source: 432}, LocInfo{Source: 1070}) のように LocInfo{Source: 432} を使いたいのですが、423,が変数aに入っている場合は、どのように取り扱えば良いでしょうか
ChatGPT
ChatGPT

変数aにLocInfo型の値が入っていて、その中のSourceフィールドの値を使いたい場合、以下のようにすれば良いでしょう:

// 変数aがLocInfo型であると仮定する
var a LocInfo

// aのSourceフィールドの値を使ってgetDijkstraPath関数を呼び出す
route, dis := getDijkstraPath(db_agent_od, LocInfo{Source: a.Source}, LocInfo{Source: 1070})

これにより、変数aのSourceフィールドの値がLocInfo構造体のインスタンスを作成する際に使用され、その値がgetDijkstraPath関数に渡されます。

2023,江端さんの技術メモ

Select文を使って、選んだ要素だけをcsvファイルにエクスポートする方法 (これから頻用しそう)

agent_db=# \copy (select * from agent_track where agent_id = 100) to test.csv with CSV;
最初の'\"が重要

最初はエクスポートです。

Microsoft Windows [Version 10.0.19044.2486]
(c) Microsoft Corporation. All rights reserved.
C:\Users\ebata>psql -U postgres -h 192.168.0.23 -p 15432
Password for user postgres:
psql (13.4, server 12.5 (Debian 12.5-1.pgdg100+1))
Type "help" for help.
postgres=# \c agent_db
psql (13.4, server 12.5 (Debian 12.5-1.pgdg100+1))
You are now connected to database "agent_db" as user "postgres".
agent_db=# \copy user_list to 'testtest.csv' WITH CSV DELIMITER ',';
COPY 20
agent_db=#
C:\Users\ebata に、testtest.csv ができています。
カラム名が必要な場合は、こちら(大抵の場合必要)。
agent_db=# \copy user_list to 'testtest.csv' WITH CSV HEADER;
次にインポートです。
以下のcsvファイルをインポートします。ファイル名はkai_20220522holyday18.csvです。
id,age,type,departure_name,departure_number,departure_lat,departure_lng,arrival_name,arrival_number,arrival_lat,arrival_lng
0,43,resident,,,34.173408,131.470684,,,34.155862,131.501246
1,24,resident,,,34.179449,131.482543,,,34.164116,131.471791
2,42,resident,,,34.168739,131.470768,,,34.160989,131.491124
3,21,resident,,,34.169494,131.469934,,,34.173498,131.471351
4,58,resident,,,34.185295,131.47414,,,34.191481,131.49456
5,48,resident,,,34.150778,131.480747,,,34.16536,131.471872
6,56,resident,,,34.16536,131.471872,,,34.174066,131.479312
7,73,resident,,,34.155731,131.500845,,,34.16776,131.472831
8,47,resident,,,34.167237,131.471785,,,34.155775,131.476531
9,21,resident,,,34.154931,131.50468,,,34.156678,131.49581
10,37,resident,,,34.16727,131.472899,,,34.171253,131.471177
11,40,resident,,,34.147241,131.474921,,,34.150675,131.486268
12,67,resident,,,34.173683,131.476347,,,34.173643,131.471027
13,28,resident,,,34.183079,131.484303,,,34.174245,131.474592
14,46,resident,,,34.146154,131.472711,,,34.159611,131.491548
15,25,resident,,,34.162497,131.489283,,,34.147212,131.475984
次に、テーブルをクリアにします。
agent_db=# delete from user_list;
DELETE 36
agent_db=# select * from user_list;
id | age | type | departure_name | departure_number | departure_lat | departure_lng | arrival_name | arrival_number | arrival_lat | arrival_lng
----+-----+------+----------------+------------------+---------------+---------------+--------------+----------------+-------------+-------------
(0 rows)
として、
agent_db=# \copy user_list from 'kai_20220522holyday18.csv' delimiter ',' csv header;
でインポートが完了します。
一応、確認します。
agent_db=# select * from user_list;
id | age | type | departure_name | departure_number | departure_lat | departure_lng | arrival_name | arrival_number | arrival_lat | arrival_lng
----+-----+----------+----------------+------------------+---------------+---------------+--------------+----------------+-------------+-------------
0 | 43 | resident | | | 34.173408 | 131.470684 | | | 34.155862 | 131.501246
1 | 24 | resident | | | 34.179449 | 131.482543 | | | 34.164116 | 131.471791
2 | 42 | resident | | | 34.168739 | 131.470768 | | | 34.160989 | 131.491124
3 | 21 | resident | | | 34.169494 | 131.469934 | | | 34.173498 | 131.471351
4 | 58 | resident | | | 34.185295 | 131.47414 | | | 34.191481 | 131.49456
5 | 48 | resident | | | 34.150778 | 131.480747 | | | 34.16536 | 131.471872
6 | 56 | resident | | | 34.16536 | 131.471872 | | | 34.174066 | 131.479312
7 | 73 | resident | | | 34.155731 | 131.500845 | | | 34.16776 | 131.472831
8 | 47 | resident | | | 34.167237 | 131.471785 | | | 34.155775 | 131.476531
9 | 21 | resident | | | 34.154931 | 131.50468 | | | 34.156678 | 131.49581
10 | 37 | resident | | | 34.16727 | 131.472899 | | | 34.171253 | 131.471177
11 | 40 | resident | | | 34.147241 | 131.474921 | | | 34.150675 | 131.486268
12 | 67 | resident | | | 34.173683 | 131.476347 | | | 34.173643 | 131.471027
13 | 28 | resident | | | 34.183079 | 131.484303 | | | 34.174245 | 131.474592
14 | 46 | resident | | | 34.146154 | 131.472711 | | | 34.159611 | 131.491548
15 | 25 | resident | | | 34.162497 | 131.489283 | | | 34.147212 | 131.475984
(16 rows)

2022/09,江端さんの技術メモ

以前、pgr_dijkstra()で、ダイクストラの順番が壊れる という内容で悩んでいて、最終的に、

utsu_tram_db3=# SELECT seq, source, edge, x1, y1 FROM pgr_dijkstra('SELECT gid as id, source, target, cost, reverse_cost FROM ways', 2, 59, directed:=false) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq;

で、順列を壊さないで、ダイクストラの表示ができる、ということを書きました。

pgr_dijkstra()で算出したノードの座標を得る方法

ところが、まだ、これでも問題が発生することが分かりました。

ノード1799からノード3342のルート計算を以下のようにやってみました。

kitaya_db=# SELECT seq, source, target, x1, y1,x2,y2, agg_cost FROM pgr_dijkstra('SELECT gid as id, source, target, length_m as cost cost, reverse_cost FROM ways', 3342, 1799, directed:=false) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq;

まあ、こんな感じで、sourceとx1,x2を追っていって、ラストのtargetとx2,y2を拾えば、いいと分かりましたので、これで大丈夫だろう、と思ってコーディングしていました。

ところが、このノード1799からノード3342を逆転させて、ノード3342からノード1799のルート計算を以下のようにやってみました。

kitaya_db=# SELECT seq, source, target, x1, y1,x2,y2, agg_cost FROM pgr_dijkstra('SELECT gid as id, source, target, length_m as cost  cost, reverse_cost FROM ways', 1799, 3342, directed:=false) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq;

と、こんな感じで、sourceが出発点にならずに、targetの方が正しい並びとなってしまっています。つまり、こんな感じ。

で、これがどっちで出てくるのか分からないので、以下のようにしました。

(1)最初のノードがsourceに出てきたら、sourceベースで読み出し、
(2)最初のノードがtargetに出てきたら、targetベースで読み出す

実装はこんな感じにしました。

type LocInfo struct {
	Lon    float64
	Lat    float64
	Source int
}

 

// 江端修正版
func getDijkstraPath(dbMap *sql.DB, locInfoStart, locInfoGoal ldarp.LocInfo) ([]ldarp.LocInfo, float64) {
	log.Println("getDijkstraPath", locInfoStart, locInfoGoal)

	var path []ldarp.LocInfo // 経路 (返り値の一つ目)
	var totalDistanceKm float64

	rowsDijkstra, errDijkstra := dbMap.Query(
		"SELECT seq,source, target, x1, y1, x2, y2, agg_cost FROM pgr_dijkstra('SELECT gid as id, source, target, length_m as cost FROM ways', $1::bigint , $2::bigint , directed:=false) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq",
		locInfoStart.Source,
		locInfoGoal.Source)

	if errDijkstra != nil {
		log.Fatal(errDijkstra)
	}
	defer rowsDijkstra.Close()

	var agg_cost float64

	isFirstCheck := true
	isSourceCheck := true

	for rowsDijkstra.Next() {

		var x1, y1, x2, y2 float64

		var seq int
		var target int

		// まずnodeを読む
		if err := rowsDijkstra.Scan(&seq, &source, &target, &x1, &y1, &x2, &y2, &agg_cost); err != nil {
			fmt.Println(err)
		}

		// 最初の1回だけ入る
		if isFirstCheck {
			if source == locInfoStart.Source {
				isSourceCheck = true
			} else {
				isSourceCheck = false
			}
			isFirstCheck = false
		}

		var loc ldarp.LocInfo

		if isSourceCheck {
			loc.Source = source
			loc.Lon = x1
			loc.Lat = y1
		} else {
			loc.Source = target
			loc.Lon = x2
			loc.Lat = y2
		}

		loc.Source = target

		path = append(path, loc)
	}

	// ラストノードだけは手入力
	path = append(path, locInfoGoal)

	totalDistanceKm = agg_cost / 1000.0
	return path, totalDistanceKm
}

もっとクールな方法があるかもしれませんが、面倒なので、戦うのはやめました。

バグを発見したので、main()を含めた再修正版をアップしておきまます。

package main

import (
	"database/sql"
	"fmt"
	"log"
	"m/src/ldarp"

	_ "github.com/lib/pq"
)

func main() {
	db, err := sql.Open("postgres",
		"user=postgres password=password host=192.168.0.23 port=15432 dbname=tomioka_db_c sslmode=disable")
	if err != nil {
		log.Fatal("OpenError: ", err)
	}
	defer db.Close()

	var a_Point, b_Point ldarp.LocInfo
	a_Point.Source = 20
	b_Point.Source = 1
	path, dist := getDijkstraPath(db, a_Point, b_Point)

	fmt.Println("path:", path)
	fmt.Println("dist:", dist)

}

// 江端再修正版
func getDijkstraPath(dbMap *sql.DB, locInfoStart, locInfoGoal ldarp.LocInfo) ([]ldarp.LocInfo, float64) {
	log.Println("getDijkstraPath", locInfoStart, locInfoGoal)

	var path []ldarp.LocInfo // 経路 (返り値の一つ目)
	var totalDistanceKm float64

	rowsDijkstra, errDijkstra := dbMap.Query(
		"SELECT seq,source, target, x1, y1, x2, y2, agg_cost FROM pgr_dijkstra('SELECT gid as id, source, target, length_m as cost cost, reverse_cost FROM ways', $1::bigint , $2::bigint , directed:=false) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq",
		locInfoStart.Source,
		locInfoGoal.Source)

	if errDijkstra != nil {
		log.Fatal(errDijkstra)
	}
	defer rowsDijkstra.Close()

	var agg_cost float64

	var loc ldarp.LocInfo
	var x1, y1, x2, y2 float64
	var seq int
	var target int
	var source int

	isFirstCheck := true
	isSourceCheck := true

	for rowsDijkstra.Next() {

		// まずnodeを読む
		if err := rowsDijkstra.Scan(&seq, &source, &target, &x1, &y1, &x2, &y2, &agg_cost); err != nil {
			fmt.Println(err)
		}

		// 最初の1回だけチェックのために入る これについては、https://wp.kobore.net/江端さんの技術メモ/post-7668/を参照のこと
		// もし rowsDijkstra.Scanで最初のsource値を読みとり、locInfoStart.Source の値と同じであれば、x1,y1をベースとして、異なる値であれば、x2,y2をベースとする

		if isFirstCheck {
			if source == locInfoStart.Source {
				isSourceCheck = true // x1, y1をベースとする処理になる
			} else {
				isSourceCheck = false // x2,y2をベースとする処理になる
			}
			isFirstCheck = false // 最初の1回をチェックすることで、2回目はこのループには入らなくなる
		}

		//var loc ldarp.LocInfo

		if isSourceCheck { // x1, y1をベースとする処理
			loc.Source = source
			loc.Lon = x1
			loc.Lat = y1
		} else { // x2,y2をベースとする処理
			loc.Source = target
			loc.Lon = x2
			loc.Lat = y2
		}
		path = append(path, loc)
	}

	// ラストノードだけは手入力 (ここは引っくり返す)
	if isSourceCheck { // x1, y1をベースとする処理
		loc.Source = target
		loc.Lon = x2
		loc.Lat = y2
	} else { // x2,y2をベースとする処理
		loc.Source = source
		loc.Lon = x1
		loc.Lat = y1
	}

	path = append(path, loc)

	totalDistanceKm = agg_cost / 1000.0
	return path, totalDistanceKm
}

で、色々問題が出てきたので、各種ケースに対応できるように追加したもの

/*
	c:\users\ebata\tomioka3b\src\others\main49.go

	このプログラムは、PostgreSQLデータベース内の地理情報を使用して最短経路を計算するためのものです。
	
	このプログラムは、以下の機能を持ちます。

	(1)main 関数内で、PostgreSQLデータベースへの接続情報を設定し、sql.Open を使用してデータベースに接続します。
	(2)getDijkstraPath 関数は、指定された始点から終点までの最短経路を計算するための関数です。この関数は、Dijkstraアルゴリズムを使用して最短経路を計算します。
	(3)LocInfo 構造体は、地理座標とノードの情報を表現します。
	(4)main 関数内で getDijkstraPath 関数を呼び出し、最短経路とその距離を計算します。異常ノードや隣接ノード、同一ノード、2つ離れたノードなど、さまざまなケースでの最短経路をテストするために、複数の呼び出しをコメントアウトしています。
	(5)getDijkstraPath 関数内では、まず指定された始点と終点に対応する地理座標を取得します。始点と終点が同一の場合は、指定されたノードの地理座標を取得します。
	(6)次に、pgr_dijkstra 関数を使用して最短経路を計算し、結果を取得します。結果は、各ノードの地理座標と距離が含まれる配列として返されます。
	(7)最後に、最短経路の全体距離を計算し、その結果を返します。


*/

package main

import (
	"database/sql"
	"fmt"
	"log"
	"os"

	_ "github.com/lib/pq"
)

type LocInfo struct {
	Lon    float64
	Lat    float64
	Source int
}

func main() {
	// PostgreSQLへの接続情報

	// Agent_od書き込み用テーブルの初期化
	db_agent_od, err := sql.Open("postgres",
		"user=postgres password=password host=192.168.0.23 port=15432 dbname=tomioka_db_e sslmode=disable") // トミオカート地図でテスト

	if err != nil {
		log.Fatal("OpenError: ", err)
	}
	defer db_agent_od.Close()

	route, dis := getDijkstraPath(db_agent_od, LocInfo{Source: 432}, LocInfo{Source: 1070}) // 異常ノード
	//route, dis := getDijkstraPath(db_agent_od, LocInfo{Source: 856}, LocInfo{Source: 688}) // 異常ノード

	//route, dis := getDijkstraPath(db_agent_od, LocInfo{Source: 723}, LocInfo{Source: 853}) // 隣接ノード
	//route, dis := getDijkstraPath(db_agent_od, LocInfo{Source: 536}, LocInfo{Source: 171}) // 隣接ノード

	//route, dis := getDijkstraPath(db_agent_od, LocInfo{Source: 536}, LocInfo{Source: 536}) // 同一ノード
	//route, dis := getDijkstraPath(db_agent_od, LocInfo{Source: 138}, LocInfo{Source: 139}) // 同一ノード
	//route, dis := getDijkstraPath(db_agent_od, LocInfo{Source: 173}, LocInfo{Source: 853}) // 2つ離れたノード
	fmt.Println("route", route, "dis", dis)

}

// テスト中
// 1行のみの場合、ヌルになるという問題と、同一ノードに対応するため
func getDijkstraPath(dbMap *sql.DB, locInfoStart, locInfoGoal LocInfo) ([]LocInfo, float64) {

	var path []LocInfo // 経路 (返り値の一つ目)
	var totalDistanceKm float64

	// 例外処理 locInfoStart.source == locInfoGoal.source の場合
	if locInfoStart.Source == locInfoGoal.Source {
		source := locInfoStart.Source

		// SQLクエリの作成
		query := fmt.Sprintf(`
		SELECT x1, y1
		FROM ways
		WHERE source = %d;
	`, source)

		// SQLクエリの実行
		var x1, y1 float64
		err := dbMap.QueryRow(query).Scan(&x1, &y1)
		if err != nil {
			log.Fatal(err)
		}

		var loc LocInfo
		loc.Source = source
		loc.Lon = x1
		loc.Lat = y1

		path = append(path, loc)

		totalDistanceKm = 0.0
		return path, totalDistanceKm
	}

	query := `
	SELECT seq, source, target, x1, y1, x2, y2, agg_cost 
	FROM pgr_dijkstra(
		'SELECT gid as id, source, target, length_m as cost FROM ways', 
		$1::bigint, 
		$2::bigint, 
		directed:=false
	) a 
	INNER JOIN ways b ON (a.edge = b.gid) 
	ORDER BY seq
	`
	//log.Println("getDijkstraPath", locInfoStart.Source, locInfoGoal.Source)

	rowsDijkstra, errDijkstra := dbMap.Query(query, locInfoStart.Source, locInfoGoal.Source)
	if errDijkstra != nil {
		log.Fatal(errDijkstra)
		os.Exit(1)
	}
	defer rowsDijkstra.Close()

	var agg_cost float64

	var loc LocInfo
	var x1, y1, x2, y2 float64
	var seq int
	var target int
	var source int

	isFirstCheck := true
	isSourceCheck := true

	count := 0

	for rowsDijkstra.Next() {
		// まずnodeを読む
		if err := rowsDijkstra.Scan(&seq, &source, &target, &x1, &y1, &x2, &y2, &agg_cost); err != nil {
			fmt.Println(err)
		}

		// 最初の1回だけチェックのために入る これについては、https://wp.kobore.net/江端さんの技術メモ/post-7668/を参照のこと
		// もし rowsDijkstra.Scanで最初のsource値を読みとり、locInfoStart.Source の値と同じであれば、x1,y1をベースとして、異なる値であれば、x2,y2をベースとする

		if isFirstCheck {
			if source == locInfoStart.Source {
				isSourceCheck = true // x1, y1をベースとする処理になる
			} else {
				isSourceCheck = false // x2,y2をベースとする処理になる
			}
			isFirstCheck = false // 最初の1回をチェックすることで、2回目はこのループには入らなくなる
		}

		//var loc LocInfo

		if isSourceCheck { // x1, y1をベースとする処理
			loc.Source = source
			loc.Lon = x1
			loc.Lat = y1
		} else { // x2,y2をベースとする処理
			loc.Source = target
			loc.Lon = x2
			loc.Lat = y2
		}
		path = append(path, loc)

		count++
	}

	// ラストノードだけは手入力 (ここは引っくり返す)
	if isSourceCheck { // x1, y1をベースとする処理
		loc.Source = target
		loc.Lon = x2
		loc.Lat = y2
	} else { // x2,y2をベースとする処理
		loc.Source = source
		loc.Lon = x1
		loc.Lat = y1
	}

	fmt.Println("count", count)

	if count == 0 { // 1行のみの場合、ヌルになるという問題に対応するため、
		loc.Source = locInfoGoal.Source
		loc.Lon = locInfoGoal.Lon
		loc.Lat = locInfoGoal.Lat

		// 入力値の指定
		source := locInfoStart.Source
		target := locInfoGoal.Source

		// SQLクエリの作成
		query := fmt.Sprintf(`
		SELECT length_m, x1, y1, x2, y2
		FROM ways
		WHERE source = %d AND target = %d;
	`, source, target)

		// SQLクエリの実行
		var length float64
		var x1, y1, x2, y2 float64
		err := dbMap.QueryRow(query).Scan(&length, &x1, &y1, &x2, &y2)
		if err != nil {
			log.Println("First attempt failed. Retrying with swapped source and target.")
			// 入れ替えたsourceとtargetの値でクエリを再実行
			query = fmt.Sprintf(`
			SELECT length_m, x1, y1, x2, y2
			FROM ways
			WHERE source = %d AND target = %d;
		`, target, source)
			err = dbMap.QueryRow(query).Scan(&length, &x1, &y1, &x2, &y2)
			if err != nil {
				log.Fatal(err)
			}
		}

		// 結果の出力
		fmt.Printf("length_m: %f\n", length)
		fmt.Printf("source: %d\n", source)
		fmt.Printf("target: %d\n", target)
		fmt.Printf("x1: %f\n", x1)
		fmt.Printf("y1: %f\n", y1)
		fmt.Printf("x2: %f\n", x2)
		fmt.Printf("y2: %f\n", y2)

		if source == locInfoGoal.Source {
			loc.Lon = x1
			loc.Lat = y1
		} else {
			loc.Lon = x2
			loc.Lat = y2
		}

		agg_cost = length

		fmt.Println("loc", loc)
	}

	path = append(path, loc)

	totalDistanceKm = agg_cost / 1000.0
	return path, totalDistanceKm
}