古いラズパイの再活用方法
古いラズパイの再活用方法
1. 背景と目的
実験システム用にTCが2台必要となったので、2019年の実証実験で使っていたラズパイを改造してTCにすることにした。
2. 今回のターゲットとしたラズパイとその構成
ディストリビューションはRaspbianである。
Linux raspberrypi 4.19.66-v7+ #1253 SMP Thu Aug 15 11:49:46 BST 2019 armv7l GNU/Linux

ラズパイにはイーサポートが1つしかないので、2つ目のポートは、USB-Ethを使う。
Cable Matters USB LAN変換アダプター 有線LANアダプター USB イーサネットアダプタ USB2.0 to RJ45

ディストリビューションはRaspbianである。
3. 古いラズパイの再構築
3.1. 状況の把握
古いラズパイを再利用するために、sudo apt-get updateをすると、
E: Failed to fetch http://raspbian.raspberrypi.org/raspbian/pool/main/c/cyrus-sasl2/libsasl2-modules_2.1.27~101-g0780600+dfsg-3+deb9u1_armhf.deb 404 Not Found [IP: 2a00:1098:0:80:1000:75:0:3 80]
E: Failed to fetch http://raspbian.raspberrypi.org/raspbian/pool/main/libv/libvpx/libvpx4_1.6.1-3+deb9u2_armhf.deb 404 Not Found [IP: 2a00:1098:0:80:1000:75:0:3 80]
という表示がでてくる。
3.2 リポジトリの更新更新
この場合リポジトリのURLが古い可能性があるので。/etc/apt/sources.listまたは/etc/apt/sources.list.d/raspi.listを編集し、最新のリポジトリURLを使用していることを確認する。
sudo nano /etc/apt/sources.list.d/raspi.list
deb行を以下のように変更する:
deb http://archive.raspbian.org/raspbian/ buster main contrib non-free rpi
3.3 パッケージリストを更新更新
リポジトリURLを変更した後、以下のコマンドを実行してパッケージリストを更新する。
アップグレード手順の例:
sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-upgrade
sudo apt-get autoremove
必要に応じて、リリースをアップグレード:
sudo sed -i 's/stretch/buster/g' /etc/apt/sources.list
sudo sed -i 's/stretch/buster/g' /etc/apt/sources.list.d/raspi.list
sudo apt-get update
sudo apt-get upgrade
以上
動画の一部を切り取ることのできる、OBS Studio
これまで、動画の一部を切り取る、という作業をしてきましたが、有償ソフトであり、他の人には紹介できない状況でした。
しかし、先日、「OBS Studio」というフリーのソフトを教えてもらい、使ってみました。
ちょっとハマったところがあったので、自分の為にメモを残しておきます(私が分かりそうな部分は、ザックリ省略します)

Step 1 OBS起動


Step 2 「ウインドウキャプチャ」




で、ここからハマった
画面の切出しが分からない。ChatGPTに聞いたところ、『Altキーを押しながらドラッグで切り出せる』と言われたんだけど、
「Altキーを押しながらドラッグ」すると、画面全体を動かし、サイズも変えることはできますが、画面の部分を指定することができません。
で、なんどか質問を繰り返して
プレビュー画面の赤枠の「端」をAltキーを押した状態でドラッグすると、その部分を切り取れます。
で分かりました。

Step 3 で、この後は、「録画開始」で切りとった部分だけが録画できるようになります。

今回は、比較的軽い「ハマり」で助かりました。
重要なのは「端」です。この一語がないだけで、ハマってしまうのが、この(IT)界隈の面倒くささです。
VMAFで万策尽きた私のためのメモ
Windowsのmsys2でもmsys64でもUbuntu22.04でもdockerでも、VMAF機能尽きのffmpegが上手くいかなくて困っています。
g:\home\ebata\vmafに
FROM ubuntu:22.04
# 必要なパッケージのインストール
RUN apt-get update && apt-get install -y \
wget build-essential yasm pkg-config libtool autoconf automake cmake git \
libx264-dev libx265-dev meson ninja-build nasm python3-pip && \
apt-get clean
# VMAFのビルドとインストール
RUN git clone https://github.com/Netflix/vmaf.git /vmaf && \
cd /vmaf/libvmaf && \
meson setup build --buildtype release && \
ninja -C build && \
ninja -C build install && \
ldconfig
# FFmpegのビルドとインストール
RUN git clone https://git.ffmpeg.org/ffmpeg.git /ffmpeg && \
cd /ffmpeg && \
./configure --enable-gpl --enable-libx264 --enable-libx265 --enable-libvmaf && \
make && make install
# 実行用のディレクトリを作成
RUN mkdir /workspace
WORKDIR /workspace
で、
docker build -t custom-ffmpeg-vmaf .
をやっても、かならず、こんなエラーが出てきて、これが、どうしても消せない。
G:\home\ebata\vmaf>docker run --rm -it -v G:\download:/workspace ffmpeg-vmaf ffmpeg -i /workspace/104014_1280x720.mp4 -i /workspace/104014_1280x720.mp4 -lavfi libvmaf="model=/workspace/vmaf_v0.6.1.json" -f null -
ffmpeg version N-118070-g17e4746687 Copyright (c) 2000-2024 the FFmpeg developers
built with gcc 11 (Ubuntu 11.4.0-1ubuntu1~22.04)
configuration: --enable-gpl --enable-libx264 --enable-libx265 --enable-libvmaf
libavutil 59. 51.100 / 59. 51.100
libavcodec 61. 27.101 / 61. 27.101
libavformat 61. 9.101 / 61. 9.101
libavdevice 61. 4.100 / 61. 4.100
libavfilter 10. 6.101 / 10. 6.101
libswscale 8. 12.100 / 8. 12.100
libswresample 5. 4.100 / 5. 4.100
libpostproc 58. 4.100 / 58. 4.100
[Parsed_libvmaf_0 @ 0x55f5cc0b97c0] could not parse model config: /workspace/vmaf_v0.6.1.json
[AVFilterGraph @ 0x55f5cc0b91c0] Error initializing filters
Error : Invalid argument
2日間かけたのですが、疲れ果てたので、闘うのを止めました。
で、代替手段です。
■ブラウザのサービスを使う方法

■VMAFを組み込んだffmpegのバイナリダウンロード
https://www.gyan.dev/ffmpeg/builds/
https://www.gyan.dev/ffmpeg/builds/#git-master-builds

ダウンロードして展開した場所の内部で(パスが通っていない)ffmpegを実行

G:\download\ffmpeg-2024-12-11-git-a518b5540d-full_build\bin>ffmpeg -i input.mp4 -i reference.mp4 -lavfi libvmaf -f null -
実行結果は以下の通り(欲しいのは以下の赤字のところだけ) # 今回は同じファイルを使って比較しただけ
G:\download\ffmpeg-2024-12-11-git-a518b5540d-full_build\bin>ffmpeg -i input.mp4 -i reference.mp4 -lavfi libvmaf -f null -
ffmpeg version 2024-12-11-git-a518b5540d-full_build-www.gyan.dev Copyright (c) 2000-2024 the FFmpeg developers
built with gcc 14.2.0 (Rev1, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libaribb24 --enable-libaribcaption --enable-libdav1d --enable-libdavs2 --enable-libopenjpeg --enable-libquirc --enable-libuavs3d --enable-libxevd --enable-libzvbi --enable-libqrencode --enable-librav1e --enable-libsvtav1 --enable-libvvenc --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxeve --enable-libxvid --enable-libaom --enable-libjxl --enable-libvpx --enable-mediafoundation --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-liblensfun --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-dxva2 --enable-d3d11va --enable-d3d12va --enable-ffnvcodec --enable-libvpl --enable-nvdec --enable-nvenc --enable-vaapi --enable-libshaderc --enable-vulkan --enable-libplacebo --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libcodec2 --enable-libilbc --enable-libgsm --enable-liblc3 --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
libavutil 59. 49.100 / 59. 49.100
libavcodec 61. 26.100 / 61. 26.100
libavformat 61. 9.100 / 61. 9.100
libavdevice 61. 4.100 / 61. 4.100
libavfilter 10. 6.101 / 10. 6.101
libswscale 8. 12.100 / 8. 12.100
libswresample 5. 4.100 / 5. 4.100
libpostproc 58. 4.100 / 58. 4.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'input.mp4':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: mp41isom
creation_time : 2024-02-14T12:54:44.000000Z
Duration: 00:00:30.46, start: 0.000000, bitrate: 2228 kb/s
Stream #0:0[0x1](und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(progressive), 722x444 [SAR 1:1 DAR 361:222], 2032 kb/s, 29.97 fps, 29.97 tbr, 29970 tbn (default)
Metadata:
creation_time : 2024-02-14T12:54:44.000000Z
handler_name : VideoHandler
vendor_id : [0][0][0][0]
encoder : AVC Coding
Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 195 kb/s (default)
Metadata:
creation_time : 2024-02-14T12:54:44.000000Z
handler_name : SoundHandler
vendor_id : [0][0][0][0]
Input #1, mov,mp4,m4a,3gp,3g2,mj2, from 'reference.mp4':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: mp41isom
creation_time : 2024-02-14T12:54:44.000000Z
Duration: 00:00:30.46, start: 0.000000, bitrate: 2228 kb/s
Stream #1:0[0x1](und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(progressive), 722x444 [SAR 1:1 DAR 361:222], 2032 kb/s, 29.97 fps, 29.97 tbr, 29970 tbn (default)
Metadata:
creation_time : 2024-02-14T12:54:44.000000Z
handler_name : VideoHandler
vendor_id : [0][0][0][0]
encoder : AVC Coding
Stream #1:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 195 kb/s (default)
Metadata:
creation_time : 2024-02-14T12:54:44.000000Z
handler_name : SoundHandler
vendor_id : [0][0][0][0]
Stream mapping:
Stream #0:0 (h264) -> libvmaf
Stream #1:0 (h264) -> libvmaf
libvmaf:default -> Stream #0:0 (wrapped_avframe)
Stream #0:1 -> #0:1 (aac (native) -> pcm_s16le (native))
Press [q] to stop, [?] for help
Output #0, null, to 'pipe:':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: mp41isom
encoder : Lavf61.9.100
Stream #0:0: Video: wrapped_avframe, yuv420p(progressive), 722x444 [SAR 1:1 DAR 361:222], q=2-31, 200 kb/s, 29.97 fps, 29.97 tbn
Metadata:
encoder : Lavc61.26.100 wrapped_avframe
Stream #0:1(und): Audio: pcm_s16le, 48000 Hz, stereo, s16, 1536 kb/s (default)
Metadata:
encoder : Lavc61.26.100 pcm_s16le
creation_time : 2024-02-14T12:54:44.000000Z
handler_name : SoundHandler
vendor_id : [0][0][0][0]
[Parsed_libvmaf_0 @ 00000226c63e6640] VMAF score: 99.992967e=N/A speed=1.57x
[out#0/null @ 00000226c61d1ac0] video:392KiB audio:5616KiB subtitle:0KiB other streams:0KiB global headers:0KiB muxing overhead: unknown
frame= 913 fps= 47 q=-0.0 Lsize=N/A time=00:00:29.95 bitrate=N/A speed=1.55x

で、この評価の見方は以下の通りです。
1. [Parsed_libvmaf_0 @ 000001f6e343da40] VMAF score: 97.465053
[Parsed_libvmaf_0 @ ...]
libvmafフィルタが処理を行ったことを示しています。VMAF score: 97.465053
最終的なVMAFスコアが97.465です。この値は、評価対象の動画(input.mp4)と参照動画(reference.mp4)の視覚的な品質の差異を数値化したものです。スコアは通常 0〜100 の範囲で表され、高いスコア(90以上)は非常に高品質であることを意味します。
2. e=N/A speed=0.299x
e=N/A
現在のところ特定のエンコードステータスがありません(通常は気にしなくて大丈夫です)。speed=0.299x
処理速度を示しています。speed=0.299xは、処理がリアルタイムの29.9%の速度で進行したことを意味します。この低速さは、VMAF計算が非常に計算集約的であるためです。
3. [out#0/null @ 000001f6e1dc98c0] video:754KiB ... muxing overhead: unknown
video:754KiB
出力された動画データの合計サイズが754KiBであることを示します。audio:0KiB subtitle:0KiB other streams:0KiB
音声、字幕、その他のストリームが含まれていないことを示します(VMAF評価ではこれらは不要)。global headers:0KiB
グローバルヘッダーのデータサイズも0KiBであることを示します。muxing overhead: unknown
出力ファイルのオーバーヘッド(余分なデータ量)が計算されていないことを示しています。nullフォーマットを使用しているため、通常は無視して問題ありません。
4. frame= 1755 fps= 18 q=-0.0 Lsize=N/A time=00:00:29.27 bitrate=N/A speed=0.299x
frame=1755
VMAF計算の対象となったフレーム数です。1755フレームが処理されました。fps=18
平均で1秒間に18フレームが処理されたことを示します。q=-0.0
映像の品質パラメータで、VMAF評価では特に重要ではありません。Lsize=N/A
出力ファイルの総サイズ(nullフォーマットなので未計算)。time=00:00:29.27
処理された動画の総時間が29.27秒であることを示します。bitrate=N/A
ビットレートが計算されていないことを示します(評価中なので未計算)。speed=0.299x
処理速度がリアルタイムの約30%であることを再度示しています。
■VMAFについての評価方法や読み方については、以下が詳しいです。
【Netflixでも使ってる!】 映像品質評価ライブラリVMAFを使ったエンコードパラメータの決定方法の紹介
C言語のGstreamerライブラリを使って、2以上の異なる種類の通信プロトコルのメトリクスを取り出す時の注意点
注意点は、通信プロトコルのメトリクスが2以上あろうとも、
main()の開始近くで
GMainLoop *main_loop = g_main_loop_new(NULL, FALSE);
を1回、
main()の終了近くで
// 共有のイベントループを実行
g_main_loop_run(main_loop);
// メインループをクリーンアップ
g_main_loop_unref(main_loop);
を、それぞれ1回だけ発行すること。
g_main_loop_run(main_loop)でプログラムは無限ループに入ってロックするので、その下のプログラムは実行されないことにも注意。
(以下はイメージ。コンパイルしても稼動しません)
#include <glib.h>
#include <gst/gst.h>
#include <pthread.h>
typedef struct {
GstElement *pipeline;
GMainLoop *main_loop; // 共有のイベントループ
} ArgsRtspRTSP;
typedef struct {
GstElement *srtserversink;
GMainLoop *main_loop; // 共有のイベントループ
} Args;
void *print_rtsp_metrics_thread(void *arg) {
ArgsRtspRTSP *args = (ArgsRtspRTSP *)arg;
// RTSPメトリクス計測のロジック
while (1) {
g_usleep(1000000); // 1秒ごとに計測
g_print("RTSP Metrics: Calculating...\n");
}
return NULL;
}
void *print_srt_metrics_thread(void *arg) {
Args *args = (Args *)arg;
// SRTメトリクス計測のロジック
while (1) {
g_usleep(1000000); // 1秒ごとに計測
g_print("SRT Metrics: Calculating...\n");
}
return NULL;
}
void initialize_rtsp_pipeline(ArgsRtspRTSP *args) {
// パイプラインの初期化
args->pipeline = gst_pipeline_new("rtsp-pipeline");
GstBus *bus = gst_element_get_bus(args->pipeline);
gst_bus_add_watch(bus, (GstBusFunc)gst_bus_async_signal_func, args->main_loop);
gst_object_unref(bus);
}
void initialize_srt_pipeline(Args *args) {
// パイプラインの初期化
args->srtserversink = gst_element_factory_make("srtserversink", "srt-sink");
GstBus *bus = gst_element_get_bus(args->srtserversink);
gst_bus_add_watch(bus, (GstBusFunc)gst_bus_async_signal_func, args->main_loop);
gst_object_unref(bus);
}
int main(int argc, char *argv[]) {
gst_init(&argc, &argv);
// 共有のイベントループを作成
GMainLoop *main_loop = g_main_loop_new(NULL, FALSE);
// RTSP引数の初期化
ArgsRtspRTSP rtsp_args;
rtsp_args.main_loop = main_loop;
initialize_rtsp_pipeline(&rtsp_args);
// SRT引数の初期化
Args srt_args;
srt_args.main_loop = main_loop;
initialize_srt_pipeline(&srt_args);
// スレッドの作成
pthread_t rtsp_thread, srt_thread;
pthread_create(&rtsp_thread, NULL, print_rtsp_metrics_thread, &rtsp_args);
pthread_create(&srt_thread, NULL, print_srt_metrics_thread, &srt_args);
// 共有のイベントループを実行
g_main_loop_run(main_loop);
// メインループをクリーンアップ
g_main_loop_unref(main_loop);
return 0;
}
ようするに、開始と終了は、1回のみ宣言する、ということ。
g_main_loop_run(main_loop) の後は、プログラムがそこでロックされて、その後のプログラムコードに進まないことに注意。
手動で収集した地物データのCSVからgeoentitydbを作るまでの手順
改めてGeoEntityDBの作り方の手順を示して下さい。
以下に、GeoEntityDB を作成する手順を改めて示します。
GeoEntityDBの作成手順
1. GeoEntityDBの作成
- PostgreSQLに接続します。
psql -U postgres
- 新しいデータベースを作成し、PostGISを有効化します。
CREATE DATABASE GeoEntityDB; \c GeoEntityDB CREATE EXTENSION postgis; CREATE EXTENSION pgrouting; -- 必要に応じて有効化
2. 地物テーブルの作成
地物データを格納するためのテーブルを作成します。
- テーブルスキーマを定義します。以下はカテゴリ付きの地物データ用のスキーマです。
CREATE TABLE geo_entities ( id SERIAL PRIMARY KEY, -- 自動生成の一意識別子 category VARCHAR(10), -- カテゴリ (例: A1, B1, D2) stay_time INTEGER, -- 滞在時間 (分) geom GEOMETRY(POINT, 4326) -- 座標データ (EPSG:4326の緯度経度) );
# id SERIAL PRIMARY KEY の部分は自動的にインポートされるので、csvの先頭の番号は行削除しておくこと
- インデックスを作成して検索性能を向上させます。
CREATE INDEX geo_entities_geom_idx ON geo_entities USING GIST (geom);
3. CSVデータのインポート
CSVファイルから地物データをインポートします。
- 一時テーブルを作成(緯度・経度データを変換するために使用)。
CREATE TEMP TABLE temp_geo_entities ( category VARCHAR(10), stay_time INTEGER, latitude DOUBLE PRECISION, longitude DOUBLE PRECISION );
- CSVをインポート(ファイルのパスは実際のCSVファイルの場所を指定してください)。
COPY temp_geo_entities (category, stay_time, latitude, longitude) FROM '/path/to/geo_entities.csv' DELIMITER ',' CSV HEADER;
# 実は、'/path/to/geo_entities.csv'の部分で酷く転けた記憶があるが、結果的にインポートできてしまったので、忘れた。
- 本テーブルに挿入(緯度・経度を
POINT型に変換して挿入)。INSERT INTO geo_entities (category, stay_time, geom) SELECT category, stay_time, ST_SetSRID(ST_MakePoint(longitude, latitude), 4326) FROM temp_geo_entities; - データが正しくインポートされたか確認します。
SELECT * FROM geo_entities LIMIT 10;
#ここから先は試していないが、メモに残しておく。
4. 既存DBとの連携設定
方法 1: postgres_fdw を使った連携
hakata_ex_cost_db(道路ネットワークDB)と GeoEntityDB を連携させます。
postgres_fdwを有効化(GeoEntityDBで実行)。CREATE EXTENSION postgres_fdw;- 外部サーバーを登録。
CREATE SERVER hakata_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (dbname 'hakata_ex_cost_db', host 'localhost');
- 外部DBへのユーザーマッピングを作成。
CREATE USER MAPPING FOR current_user SERVER hakata_server OPTIONS (user 'postgres', password 'your_password');
- 必要なテーブルをインポート(例: 道路ネットワークテーブル)。
IMPORT FOREIGN SCHEMA public FROM SERVER hakata_server INTO public;
- テーブルが使用可能か確認。
SELECT * FROM ways LIMIT 10;
5. 経路計算の設定
最短経路計算の基本例
エージェントの現在地から目的地(地物)までの経路を計算します。
- エージェントの現在地に最も近いノードを取得。
SELECT id FROM ways_vertices_pgr ORDER BY geom <-> ST_SetSRID(ST_MakePoint(130.3980676, 33.59543503), 4326) LIMIT 1;
- 地物の位置に最も近いノードを取得。
SELECT id FROM ways_vertices_pgr ORDER BY geom <-> (SELECT geom FROM geo_entities WHERE id = 1) LIMIT 1;
- Dijkstraアルゴリズムで経路計算。
SELECT seq, id1 AS node, id2 AS edge, cost FROM pgr_dijkstra( 'SELECT id, source, target, cost FROM ways', start_node_id, end_node_id, directed := false );
6. 地物データと経路計算の組み合わせ
以下のようなクエリで、地物データの位置と経路計算を連携させます。
- エージェントの現在地から15メートル以内の地物を検索(これ、上手く稼動しません。下の方は使って下さい)。
SELECT id, category, stay_time, ST_Distance(geom, ST_SetSRID(ST_MakePoint(130.3980676, 33.59543503), 4326)) AS distance FROM geo_entities WHERE ST_DWithin(geom, ST_SetSRID(ST_MakePoint(130.3980676, 33.59543503), 4326), 15);
- 発見した地物への最短経路を計算。
SELECT seq, id1 AS node, id2 AS edge, cost FROM pgr_dijkstra( 'SELECT id, source, target, cost FROM ways', start_node_id, (SELECT id FROM ways_vertices_pgr ORDER BY geom <-> (SELECT geom FROM geo_entities WHERE id = found_geo_id) LIMIT 1), directed := false );
まとめ
- GeoEntityDBの作成とデータインポート:
- PostGISとpgRoutingを有効化し、CSVデータを地物テーブルに格納。
- 既存DBとの連携:
postgres_fdwを使用して、hakata_ex_cost_dbの道路ネットワークを参照。
- 経路計算の実装:
- エージェントの現在地と地物データを連携させ、道路ネットワーク上で最短経路を計算。
これにより、エージェントが嗜好に応じて地物に向かう動きをリアルにシミュレーションする仕組みを構築できます。
ーーーーー
こういうミスもやったので、以下のメモに残しておく。
SELECT * FROM geo_entities WHERE category = 'B1'; としたらエントリーがでてきませんが、
SELECT * FROM geo_entities WHERE category = ' B1'; としたらエントリーがでてきました。
categoryの要素でえ最初に空白が入っているものを修正したいのですが、どうしたら良いでしょうか
category の要素の先頭に空白が含まれている場合、それを修正するには TRIM 関数を使用して先頭と末尾の空白を取り除く必要があります。以下に、データを修正する方法を説明します。
手順
1. データの確認
まず、空白が含まれているデータを確認します。
LIKE ' %'は先頭に空白があるデータを検索します。LIKE '% 'は末尾に空白があるデータを検索します。
2. 空白を修正
TRIM 関数を使用して、category カラムから空白を取り除きます。
すべてのエントリを修正する場合
以下のクエリで、空白を削除した値に更新します。
3. 修正結果の確認
修正後に、データが正しく更新されたか確認します。
- クエリの結果が空であれば、すべての空白が正常に削除されたことを意味します。
4. 再度クエリを実行
修正後、以下のようにクエリを実行して正しいデータが取得できるか確認します。
補足
TRIMの動作:TRIM関数は、先頭と末尾の両方から空白を削除します(不要なスペースも含む)。- データの一時バックアップ: データ更新は破壊的変更となるため、念のため更新前にバックアップを取得しておくことをおすすめします。
これで問題が解決するはずです!もしさらなるサポートが必要であればお知らせください。
ざっくりとした使い方
geoentitydb=# \l
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges
--------------------+----------+----------+-----------------+------------+------------+------------+-----------+-----------------------
geoentitydb | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |
hakata_db | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |
hakata_ex_cost_db | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |
geoentitydb=# SELECT id, category, stay_time, geom FROM geo_entities where id = 29;
id | category | stay_time | geom
----+----------+-----------+----------------------------------------------------
29 | B1 | 20 | 0101000020E610000006802A6E9C4C6040099FB5F44BCB4040
geoentitydb=# SELECT id, category, stay_time, ST_X(geom) AS longitude, ST_Y(geom) AS latitude FROM geo_entities where id = 29;
id | category | stay_time | longitude | latitude
----+----------+-----------+-------------+-------------
29 | B1 | 20 | 130.3940955 | 33.58825549
こんな感じででてくる。
以下は、130.4064827799044, 33.591348965846656 から 100メートル以内のジオエンティティを抽出するSQLです。(こっちが正しい)
geoentitydb=# SELECT id, category, stay_time, ST_Distance(geom::geography, ST_SetSRID(ST_MakePoint(130.4064827799044, 33.591348965846656), 4326)::geography) AS distance FROM geo_entities WHERE ST_DWithin(geom::geography, ST_SetSRID(ST_MakePoint(130.4064827799044, 33.591348965846656), 4326)::geography, 100);
id | category | stay_time | distance
------+----------+-----------+-------------
304 | A2 | 60 | 68.03775787
357 | A3 | 120 | 75.80618279
366 | A3 | 120 | 76.48825161
414 | D2 | 180 | 80.65210758
415 | D2 | 180 | 79.93815148
429 | D2 | 180 | 61.23626794
435 | D2 | 180 | 62.50441807
546 | E1 | 480 | 77.36793898
550 | E1 | 480 | 90.09868468
813 | E2 | 300 | 54.40053345
1174 | F2 | 10 | 92.85196166
1217 | F2 | 10 | 89.44133931
1933 | H2 | 30 | 64.5772006
1942 | H2 | 30 | 84.70916514
(14 rows)
エクスプローラで、ファイルをコピーする場合、同じファイル名の場合には時刻が新しい場合のみ上書きしたいですがどうすれば良いですか
エクスプローラで、ファイルをコピーする場合、同じファイル名の場合には時刻が新しい場合のみ上書きしたいですがどうすれば良いですか
ChatGPT
ChatGPT
Windowsエクスプローラでファイルをコピーする際、同じファイル名の場合は時刻が新しい場合のみ上書きするという機能は標準では提供されていません。しかし、以下の方法でこの操作を実現できます。
方法1: コマンドプロンプトでrobocopyを使用
Windowsにはrobocopyという強力なコマンドラインツールがあり、これを使用してファイルのコピーを行うと、指定した条件に基づいてファイルを上書きできます。
以下の手順で行います。
コマンドプロンプトを管理者権限で開く
以下のrobocopyコマンドを実行
cmd
コードをコピーする
robocopy "コピー元フォルダのパス" "コピー先フォルダのパス" /XO
ここで、/XOオプションは古いファイルを除外します。これにより、コピー元フォルダのファイルがコピー先フォルダのファイルより新しい場合にのみ上書きされます。
例
コピー元フォルダがC:\source、コピー先フォルダがD:\destinationの場合、次のように入力します。
cmd
コードをコピーする
robocopy "C:\source" "D:\destination" /XO
cmd.exeを管理者モードで立ち上げ
C:\WINDOWS\system32>robocopy "f:\しゅらばしゅう" "\\192.168.0.23\ynu-share\しゅらばしゅう_フルバックアップ" /XO /E /Z
C:\WINDOWS\system32>robocopy "\\192.168.0.10\usb_mount" "\\192.168.0.23\ynu-share\usb_mount_フルバックアップ" /XO /E /Z
======
robocopy "f:\しゅらばしゅう" "\\192.168.0.23\ynu-share\しゅらばしゅう_フルバックアップ" /XO /E /Z
robocopy "\\192.168.0.10\usb_mount" "\\192.168.0.23\ynu-share\usb_mount_フルバックアップ" /XO /E /Z
robocopy "g:home\ebata\tomioka3B\src" "\\192.168.0.9\share\tomioka3B\src" /XO /E /Z
robocopy "g:home\ebata\hakata" "\\192.168.0.9\share\hakata" /XO /E /Z
echo
======
距離計算を別途計算するようにした、getDijkstraPath 最新版
江端がずっとメンテナンスしているgetDijkstraPath関数は、costを変更することによって、ダイクストラ計算を引き曲げているので、距離が正確に出せない。
そこで、agg_costを使わずに、agg_length_mというものを作って、強制的に距離を作り出すようにコードを変更した。
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 {
fmt.Println("tools.go line 204 error occures")
//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
}
// こちらは、cost, reverse_cost を使っている
query := `
SELECT seq, source, target, x1, y1, x2, y2, agg_cost, length_m
FROM pgr_dijkstra(
'SELECT gid as id, source, target, cost, reverse_cost FROM ways',
$1::bigint,
$2::bigint,
directed:=false
) a
INNER JOIN ways b ON (a.edge = b.gid)
ORDER BY seq
`
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 length_m float64
agg_length_m := 0.0
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, &length_m); err != nil {
fmt.Println(err)
}
agg_length_m += length_m
// fmt.Println("length_m", length_m, "agg_length_m", agg_length_m)
// 最初の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
}
// なんかprg_dijkstraが変な値を返す場合があるので、その対応(ロジカルではないが、パッチ的に対応)
if loc.Source == locInfoGoal.Source {
path = append(path, loc)
}
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) // 諦める。とりあえず、エラーを返す
return nil, 0.0
}
}
// 結果の出力
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) // 354
} // if count == 0
//totalDistanceKm = agg_cost / 1000.0
totalDistanceKm = agg_length_m / 1000.0
return path, totalDistanceKm
}
パソコンでHDMI接続に失敗し、やむなくiPadのパワポでプレゼンしなければならなくなった時の対応
前提
- iPad用HDMI変換ポートを購入(純正は高いが、iPhoneでネトフリ見る時など重宝しているので、純正の購入をお勧めします)
- PowerPointのレビュー専用のアプリをインストールしておくこと(レビュー専用ならタダ)
- 資料はmail経由などを使ってiPadにダウンロードしておく
手順
Step 1 "ファイルのフォルダを開く"

Step 2 ファイルを開く

Step 3 ここをクリックする

Step 4 こういう画面が出てくる(場合によっては、プレゼンテーション画面そのものが出てくることもある)

Step 5 ディスプレイ/プロジェクタの方にプレゼンテーションモードで表示される(といいな)

Step 5 "Step 4"の画面を操作すると、画面が変わる(ちなみに、画面の左下の方を叩くとページが前に戻り、右下の方を叩くとページが次に動く
博多駅から西に抜けるwayを手動で作ってみた
博多駅のNodeを選んで、GIS-DBを作りなおしてみたが、博多駅から西に抜けるwayがなかったので、抜けるNodeに接続するレコードを手動で作った。
gidとosm_idは衝突しない数値を選んで、tag_idは、既存の数値を選ぶ(選ばないと弾かれる)
あとは、sourceとtargetからsource_osm、target_osm とそれぞれの座標を引っ張って、あとは、chatGPTに座標を入れると2点間の距離を教えてくれる。
この距離が、 length_m となる。 lengthは、length_m / 92854.2986 の値になる。 length = cost = reverse_cost となる。 cost_s, reverse_cost_sは、時間を含めたコストらしいが、cost x 6685.509499 の値を入れておいた(まあ、どうでも言いのだろう)
あとthe_geomの値も、ChatGPTが教えてくれる。このSQL文を教えて貰った。
SELECT ST_AsEWKB(ST_MakeLine(
ST_SetSRID(ST_MakePoint(130.4210743, 33.5897456), 4326),
ST_SetSRID(ST_MakePoint(130.4206351, 33.5899416), 4326)
)) AS the_geom_hex;
で、最終的に、こうなった。
INSERT INTO ways (gid, osm_id, tag_id, length, length_m, name, source, target, source_osm, target_osm, cost, reverse_cost, cost_s, reverse_cost_s, rule, one_way, oneway, x1, y1, x2, y2, maxspeed_forward, maxspeed_backward, priority, the_geom)
VALUES (200001, 2000000001, 501, 0.000497554, 46.2, 'ebata', 44351 , 89839, 1882462094, 7992560451, 0.000497554, 0.000497554, 3.3264, 3.3264, NULL, 0, 'UNKNOWN', 130.4210743 , 33.5897456 , 130.4206351 , 33.5899416 , 10 , 10 , 0 , '0102000020E610000002000000f475CF70794D6040DE7AA8C87CCB404097c1BDD7754D60406446D33483CB4040');
で、上手く動いているようです。
SELECT seq, edge, b.the_geom AS "the_geom" FROM pgr_dijkstra('SELECT gid as id, source, target, cost, reverse_cost FROM ways', 51913,23498) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq;
で

として、
