未分類

(その1)

私が高校生の頃だったと思います。

ロシア(当時のソビエト連邦共和国)の領域を通過した、韓国の民間航空機がソビエト軍の戦闘機に撃墜されるという事件がありました。

後に「大韓航空機撃墜事件」と呼ばれる事件です。

今になってみると、冷戦はその後半部を終了し、数年後に終結を迎えることになっていたのですが、当時の誰もがそのようなことを予想すべくもなく、世界はこの惨事に声を失なったものです。

その後、この事件は、各国の主張が入り乱れて、何が何だかか分からない状況を呈していました。

先ずは、ソビエト側による否定、事実の隠蔽に始まり、日本の自衛隊による通信記録の傍受の公開、米国による追訴、そして、最後にアンドレイ・グロムイコ外務大臣の「大韓航空機は民間機を装ったスパイであった」という声明の発表に至ります。

当時、私は、民間人(296人)を満載した戦闘能力を全く有しない民間機を、正規軍の戦闘機が撃墜したという事実に対するショックに加え、人類として到底認容しえないこのような非道な暴力が、軍事的な観点からは『十分に認容され得る』という事実に、衝撃を受けたものです。

-----

大学に入学した後、私はいわゆる学寮達と共に議論を行うことを常とする「政治に極めて意識の高い自治寮」に入寮し、色々なことで(愚にもつかぬことも多かったですが)議論を闘わせたりしていました。

大学2年の夏休みに実家に帰省した私が、父の運転する助手席に座っていた時だったと思います。

どういう経緯で父とこの事件(大韓航空機撃墜事件)について話を始めたか覚えていませんが、その頃、少しだけ聞き齧った地政学の本の話を引用して、『この事件のソビエト側の対応を、認容できる部分もある』という論を展開していました。

私は、---- もし、『大韓航空機 = スパイ機』であったとすれば、または、その恐れがあったとすれば、例え、それが航空機の整備不良による事故であったとしても、ソビエトが国防の観点から、そのスパイ機の可能性がある旅客機を撃墜することは、ある意味仕方がなかったのでは------ と、自分の考えを言いました。

父は、私とそのような話をする時には、いつも嬉しそうに話の間の手を入れ、私の話がどんなに稚拙でも、その話の腰を折ることなく、最後まで聞き入れてくれたものでした。

その時の父は、いつもの父とは違う様子で、ハンドルを握りながら、フロントガラスを見つめながら言いました。

『智一。そうではない』

私は、少し驚いて父の横顔を見ました。

『航空機が民間機である以上、如何なる理由があっても、民間機を撃墜するということは許されない』

私は意外な感じを受けながらも、父に反論しました。

『もし、韓国機がソビエトの領空をスパイ目的で侵犯しており、国益を脅かすことが明らかであったとしても?』

父は、息子にキッパリと言いました。

『仮に、その航空機の機長を含め、また仮に乗客のほとんどがスパイであって、悪意の目的をもって、領空侵犯をしたとしてもだ。

そこにたった一人の無関係の乗客が乗っているのであれば、どのような者であれ、その命を奪う行為を正当化することはできない』

唖然としている息子に、父は静かに続けました。

『それが「人間」と言うもの、「命」と言うものではないか?」

それは、恐らく、沢山の命が奪われる現実を間の当りにしてきた者だけが持ち得る、魂から絞り出された言葉だったのだと思います。

-----

私は、サイドウインドウの方を向いて、そのまま黙り込みました。

胸の中に広がる熱いものに突かれて潤んできた眼を、父に見られたくなったからです。

----------------------

(その2)

私は2000年から2年間、仕事で米国に在住する機会を得ました。

この時、私が幸運だったことは、日本からチームとして参加することになり、チームのメンバの内、所帯を有するものは、家族とともに米国に滞在することになったこと。

そして、その家族の方々がいずれも気持よ良い方ばかりで、多くの交友を深める機会を得ることができたことでした。

-----

ある休日のこと、私は自分の担当していたプログラムの数行を修正する為に、休日に家族をつれて職場に出ました。そこで、私の先輩とその奥様に出会いました。

奥様は、当時、皇太子のご令嬢の誕生を、インターネットで確認する為に、先輩とともに職場に来られたとのこと。 (コロラドの少くともフォートコリンズという地域には、日本語向けのテレビ放送はなかった)

その時、私は『はあ、そうですか』としか応えられませんでした。

-----

私は、一度、無礼を承知で、奥様にお伺いしたことがあります。

『芸能人の誰かが結婚したとか離婚したとか、そういう週刊誌に掲載されるような内容は、興味の対象になり得るものでしょうか』と。

これに対して、奥様は、「トム(Tom)」(赴任先では、全員がこのような愛称で呼び会うことが、赴任先の会社のルールとして統一されていた)と、私をお呼びになられた後、次のように続けられました。

『あなたは、自分の大切な家族や友人が、幸せになったり不幸になったりした時に、それを「興味の外」ととして置いとけるものでしょうか?』

『いえ、そのようなことはないと思います。家族や友人を思い、同じように、喜び、または悲しむと思います。また、そのような人間で在りたいと思います』

『私も同じです。私も友人達の不幸に悲しみ、幸福に喜んでいるだけです』

しかし・・・と言いかけた私を制して、奥様は続けられました。

『違いがあるとすれば、私はその友人を良く知っているのですが、逆に、その友人は私のことを全然知らない、という、ただそれだけのことなのです』

-----

私は、このパラダイムを提示された時の衝撃を忘れられません。

冗談でも、揶揄でもなく------、私は今でも、この奥様のことを尊敬できる人々のお一方であり、心から敬愛すべき対象であると、固く信じております。

(本文章は、全文を掲載し内容を一切変更せず著者を明記する限りにおいて、 転載して頂いて構いません。)

未分類

python --version、 python3 --version のいずれも、"Python"とだけリターンが戻ってきて終了します。

コントロールパネルを開きます。
プログラムまたはプログラムと機能を選択します。
プログラムの一覧からPythonを探し、選択します。
アンインストールをクリックします。多くの場合、Pythonの各バージョンが個別にリストアップされているので、削除したいものをすべてアンインストールする必要があります。
指示に従ってアンインストールプロセスを完了します。

ステップ 1: Pythonのダウンロード
Pythonの公式ウェブサイトにアクセスします。
「Download Python 3.x.x」(xは最新バージョン番号を示します)をクリックして、インストーラーをダウンロードします。
ステップ 2: インストーラーの実行
ダウンロードしたインストーラー(例:python-3.x.x.exe)を実行します。

インストールの初期画面で「Add Python 3.x to PATH」のチェックボックスを選択します(これにより、コマンドラインからPythonを実行できるようになります)。

「Install Now」をクリックして、インストールを開始します。

注: より詳細な設定を行いたい場合は、「Customize installation」を選択して、インストールオプションをカスタマイズできます。
ステップ 3: インストールの確認
インストーラーが完了すると、Pythonが正常にインストールされたことを確認するために、以下の手順でバージョン情報をチェックすることができます:

スタートメニューを開き、「cmd」とタイプしてコマンドプロンプトを開きます。

コマンドプロンプトで以下のコマンドを入力して、インストールされたPythonのバージョンを確認します。

css
Copy code
python --version
または

css
Copy code
python3 --version
ステップ 4: pipの確認
pipはPythonでパッケージを管理するためのツールで、Pythonと一緒にインストールされるはずです。以下のコマンドでpipがインストールされているかを確認できます:

css
Copy code
pip --version
もしpipがない場合や問題がある場合は、pipを手動でインストールする必要がありますが、Pythonのインストーラーを使って正常にインストールが行われた場合は、自動的にpipもインストールされるはずです。

これらのステップを完了すると、Windows 11にPythonがインストールされ、使用を開始することができます。

ーーーーー

ところが状況が改善されない!

Microsoftのページに飛んで、そこのボタンを押して入れた(なんだか良くわからんが、まあ、そのまま放置した)

$ python3 --version
Python 3.11.6

と出てきているが、まあ、なんでもいいや。

ーーーーー

tomoi@DESKTOP-NHQLQU4 MSYS ~
$ pwd
/c/Users/tomoi

tomoi@DESKTOP-NHQLQU4 MSYS ~
$ mkdir -p ome_config

tomoi@DESKTOP-NHQLQU4 MSYS ~
$ cd ome_config/

OvenMediaEngineのDockerの起動

docker run -d -p 1935:1935 -p 3333:3333 -p 8080:8080 -p 9000:9000 -p 9998:9998/udp -p 9999:9999/udp -p 10000:10000/udp -p 10001:10001/udp -v /c/Users/tomoi/one_config:/opt/ovenmediaengine/bin/origin_conf --name ovenmediaengine airensoft/ovenmediaengine:latest

Run OvenPlayerのDockerの起動

$ docker run -d -p 8090:80 airensoft/ovenplayerdemo:latest

とりあえず、少なくとも、http://localhost:8090/で、以下は出てくる

 

未分類

まだ問題山積だけど、golangのCSVパースが通った。

package main

import (
	"encoding/csv"
	"encoding/json"
	"fmt"
	"strings"
)

type Location struct {
	Lat float64 `json:"lat"`
	Lng float64 `json:"lng"`
}

type Record struct {
	ID         int        `json:"id"`
	UserID     int        `json:"user_id"`
	Distance   float64    `json:"distance"`
	Place      []Location `json:"place"`
	Speed      []float64  `json:"speed"`
	Prediction int        `json:"prediction_type"`
	StartAt    string     `json:"start_at"`
	EndAt      string     `json:"end_at"`
}

func main() {
	// 提供されたCSVデータ
	csvData := `50,7,53.04935656638487,"[{""lat"": 34.6653, ""lng"": 135.2241}, {""lat"": 34.6653, ""lng"": 135.2241}, {""lat"": 34.6652, ""lng"": 135.2241}, {""lat"": 34.6652, ""lng"": 135.224}, {""lat"": 34.6651, ""lng"": 135.224}, {""lat"": 34.665, ""lng"": 135.224}, {""lat"": 34.6649, ""lng"": 135.2239}]","[0.0, 0.0001, 0.0002, 0.0002, 0.0004, 0.0004, 0.0004]",1,2023-03-03 07:40:00,2023-03-03 07:43:00`

	// CSVデータを読み込む
	r := csv.NewReader(strings.NewReader(csvData))
	record, err := r.Read()
	if err != nil {
		fmt.Println("CSVデータを読み込めません:", err)
		return
	}

	// CSVデータをパース
	id := record[0]
	userID := record[1]
	distance := record[2]
	placeJSON := record[3]
	speedJSON := record[4]
	prediction := record[5]
	startAt := record[6]
	endAt := record[7]

	// JSONデータをパース
	var place []Location
	if err := json.Unmarshal([]byte(placeJSON), &place); err != nil {
		fmt.Println("Placeデータをパースできません:", err)
		return
	}

	var speed []float64
	if err := json.Unmarshal([]byte(speedJSON), &speed); err != nil {
		fmt.Println("Speedデータをパースできません:", err)
		return
	}

	// パースしたデータを表示
	fmt.Println("ID:", id)
	fmt.Println("User ID:", userID)
	fmt.Println("Distance:", distance)
	fmt.Println("Place:", place)

	// Placeを要素単位で表示
	fmt.Println("Place:")
	for _, loc := range place {
		fmt.Printf("Lat: %.4f, Lng: %.4f\n", loc.Lat, loc.Lng)
	}

	fmt.Println("Speed:", speed)

	// Speedを要素単位で表示
	fmt.Println("\nSpeed:")
	for i, s := range speed {
		fmt.Printf("Index %d: %.4f\n", i, s)
	}

	fmt.Println("Prediction Type:", prediction)
	fmt.Println("Start At:", startAt)
	fmt.Println("End At:", endAt)
}


 

出力結果

ID: 50
User ID: 7
Distance: 53.04935656638487
Place: [{34.6653 135.2241} {34.6653 135.2241} {34.6652 135.2241} {34.6652 135.224} {34.6651 135.224} {34.665 135.224} {34.6649 135.2239}]
Place:
Lat: 34.6653, Lng: 135.2241
Lat: 34.6653, Lng: 135.2241
Lat: 34.6652, Lng: 135.2241
Lat: 34.6652, Lng: 135.2240
Lat: 34.6651, Lng: 135.2240
Lat: 34.6650, Lng: 135.2240
Lat: 34.6649, Lng: 135.2239
Speed: [0 0.0001 0.0002 0.0002 0.0004 0.0004 0.0004]

Speed:
Index 0: 0.0000
Index 1: 0.0001
Index 2: 0.0002
Index 3: 0.0002
Index 4: 0.0004
Index 5: 0.0004
Index 6: 0.0004
Prediction Type: 1
Start At: 2023-03-03 07:40:00
End At: 2023-03-03 07:43:00

未分類

CREATE TABLE trips_test (
id serial PRIMARY KEY,
user_id integer,
distance double precision,
place point[],
speed double precision[],
prediction_type integer,
start_at timestamp,
end_at timestamp
);

INSERT INTO trips_test (user_id, distance, place) VALUES (1, 24.66249845798845, ARRAY[point '(34.7241, 135.1294)', point '(34.7241, 135.1294)', point '(34.7241, 135.1294)']);

moove=# INSERT INTO trips_test (user_id, distance, place)
moove-# VALUES (1, 24.66249845798845, ARRAY[point '(34.7241, 135.1294)', point '(34.7241, 135.1294)', point '(34.7241, 135.1294)']);
INSERT 0 1
moove=# select * from trips_test;
id | user_id | distance | place | speed | prediction_type | start_at | end_at
----+---------+-------------------+------------------------------------------------------------------+-------+-----------------+----------+--------
1 | 1 | 24.66249845798845 | {"(34.7241,135.1294)","(34.7241,135.1294)","(34.7241,135.1294)"} | | | |
(1 row)

 

上記のテーブルから、座標情報を一つ一つ取り出すにはどうしたら良いですか

 

SELECT id, user_id, unnest(place) AS single_point
FROM trips_test;

 

moove=# SELECT id, user_id, unnest(place) AS single_point
moove-# FROM trips_test;
id | user_id | single_point
----+---------+--------------------
1 | 1 | (34.7241,135.1294)
1 | 1 | (34.7241,135.1294)
1 | 1 | (34.7241,135.1294)

 

 

 

未分類

Ubuntu 22.04にdockerをインストールする

を参考にして頂きつつ、インストール実施中。

私は、根拠なく、

$ sudo apt-get install docker-ce=5:24.0.4-1~ubuntu.22.04~jammy

を選びました。

ラズパイ4、Ubuntu22.04、で"docker compose"も動いているようです ーー "docker-compose"は動きません。

 

未分類

-- カスタムプロトコルの識別子(任意の値を設定)

local NX_PROTOCOL = Proto("NXDlink", "nxdlink protocol dissector")

 

-- プロトコルのフィールド定義

local nx_protocol_fields = NX_PROTOCOL.fields

nx_protocol_fields.magic = ProtoField.string("nx_protocol.magic", "Magic Number")

nx_protocol_fields.message_length = ProtoField.uint32("nx_protocol.message_length", "Message Length")

nx_protocol_fields.source_address = ProtoField.uint32("nx_protocol.source_address", "Source Address")

nx_protocol_fields.destination_address = ProtoField.uint32("nx_protocol.destination_address", "Destination Address")

nx_protocol_fields.timestamp = ProtoField.uint32("nx_protocol.timestamp", "Timestamp")

nx_protocol_fields.message_number = ProtoField.uint32("nx_protocol.message_number", "Message Number")

nx_protocol_fields.m_ctl = ProtoField.uint32("nx_protocol.m_ctl", "M_CTL")

nx_protocol_fields.inq_id = ProtoField.string("nx_protocol.inq_id", "INQ_ID")

nx_protocol_fields.tcd = ProtoField.uint32("nx_protocol.tcd", "TCD")

nx_protocol_fields.ver = ProtoField.uint32("nx_protocol.ver", "VER")

nx_protocol_fields.gtid = ProtoField.string("nx_protocol.gtid", "GTID")

nx_protocol_fields.mode = ProtoField.uint32("nx_protocol.mode", "MODE")

nx_protocol_fields.pver = ProtoField.uint32("nx_protocol.pver", "PVER")

nx_protocol_fields.pri = ProtoField.uint32("nx_protocol.pri", "PRI")

nx_protocol_fields.cbn = ProtoField.uint32("nx_protocol.cbn", "CBN")

nx_protocol_fields.tbn = ProtoField.uint32("nx_protocol.tbn", "TBN")

nx_protocol_fields.bsize = ProtoField.uint32("nx_protocol.bsize", "BSIZE")

nx_protocol_fields.fui = ProtoField.uint32("nx_protocol.fui", "FUI")

-- nx_protocol_fields.fc = ProtoField.uint32("nx_protocol.fc", "FC", base.HEX)

nx_protocol_fields.fc = ProtoField.uint32("nx_protocol.fc", "FC")

nx_protocol_fields.field_x =  ProtoField.uint32("nx_protocol.field_x", "FIELD_X")

nx_protocol_fields.field_y =  ProtoField.uint32("nx_protocol.field_y", "FIELD_Y")

nx_protocol_fields.field_z =  ProtoField.uint32("nx_protocol.field_z", "FIELD_Z")

 

-- カスタムプロトコルの識別子(任意の値を設定)

--local DATA_PROTOCOL = Proto("DATALink", "datalink protocol dissector")

-- プロトコルのフィールド定義

--local data_protocol_fields = DATA_PROTOCOL.fields

--data_protocol_fields.fc = ProtoField.uint32("data_protocol.fc", "FC")

 

 

 

 

-- パケット解析関数

function NX_PROTOCOL.dissector(buffer, pinfo, tree)

-- フレーム情報を表示

pinfo.cols.protocol:set("NX Protocol")

local subtree = tree:add(NX_PROTOCOL, buffer(), "NX Protocol Data")

 

-- "NUXM" ヘッダ文字列を確認

local data = buffer():string()

-- local offset = data:find("NUXM")

local offset = data:find("NUXM") -1 -- 1を引かないと、ずれる

if not offset then

-- "NUXM" が見つからない場合は解析を中止

return

end

 

-- ヘッダ情報を解析

 

subtree:add(nx_protocol_fields.magic, buffer(offset, 4))

subtree:add(nx_protocol_fields.message_length, buffer(offset  + 4, 4))

subtree:add(nx_protocol_fields.source_address, buffer(offset  + 8, 4))

subtree:add(nx_protocol_fields.destination_address, buffer(offset + 12, 4))

subtree:add(nx_protocol_fields.timestamp, buffer(offset + 16, 4))

subtree:add(nx_protocol_fields.message_number, buffer(offset + 20, 4))

subtree:add(nx_protocol_fields.m_ctl, buffer(offset + 24, 4))

subtree:add(nx_protocol_fields.inq_id, buffer(offset + 28, 12))

subtree:add(nx_protocol_fields.tcd, buffer(offset + 40, 2))

subtree:add(nx_protocol_fields.ver, buffer(offset + 42, 2))

subtree:add(nx_protocol_fields.gtid, buffer(offset + 44, 8))

subtree:add(nx_protocol_fields.mode, buffer(offset + 52, 2))

subtree:add(nx_protocol_fields.pver, buffer(offset + 54, 1))

subtree:add(nx_protocol_fields.pri, buffer(offset + 55, 1))

subtree:add(nx_protocol_fields.cbn, buffer(offset + 56, 1))

subtree:add(nx_protocol_fields.tbn, buffer(offset + 57, 1))

subtree:add(nx_protocol_fields.fui, buffer(offset + 60, 4))

subtree:add(nx_protocol_fields.fc, buffer(offset + 64, 2))

 

-- fcの値を取得

-- local fc_value = buffer(offset + 64, 2):le_uint() -- リトルエンディアンの場合

local fc_value = buffer(offset + 64, 2)-- リトルエンディアンの場合

 

-- デバッグ情報を表示

-- print("fc_value:", string.format("0x%04X", fc_value)) -- 16進数表示

 

 

 

-- fcの値に応じてフィールドを追加

-- if fc_value == 0x181 then

-- if fc_value == 385 then

if fc_value = 0 then

-- 0x181の場合、特定のフィールドを追加

subtree:add(nx_protocol_fields.field_x, buffer(offset + 66, 4))

elseif fc_value == 0x183 then

-- 0x183の場合、別のフィールドを追加

subtree:add(nx_protocol_fields.field_y, buffer(offset + 66, 4))

elseif fc_value == 0x188 or fc_value == 0x189 then

-- 0x188または0x189の場合、共通のフィールドを追加

subtree:add(nx_protocol_fields.field_z, buffer(offset + 66, 4))

else

-- その他の場合、何もしない

end

 

 

end

 

--function DATA_PROTOCOL.dissector(buffer, pinfo, tree)

-- フレーム情報を表示

--   pinfo.cols.protocol:set("DATA Protocol")

--   local subtree = tree:add(DATA_PROTOCOL, buffer(), "DATA Protocol Data")

--   subtree:add(data_protocol_fields.fc, buffer(64, 2))

--end

 

 

-- プロトコルをWiresharkに登録

local udp_table = DissectorTable.get("udp.port")

udp_table:add(10631, NX_PROTOCOL) -- UDPポート10631でカスタムプロトコルを解析

udp_table:add(40311, NX_PROTOCOL) -- UDPポート40311でカスタムプロトコルを解析

udp_table:add(40312, NX_PROTOCOL) -- UDPポート40312でカスタムプロトコルを解析

 

 

未分類

ラプラス分布は、ランデル分布とも言われて、交通工学のシミュレーションでは非常に重要な確率分布として使われているようです。

パラメータとして、平均(location parameter)を表す μ と尺度(scale parameter)を表すbを持ち、ラプラス分布の確率密度関数は、 f(xμ,b)=1/2b exp(bxμ∣​) と表される。

でもって、

交通シミュレーションで使われる場面

■交通の到着間隔のモデリング → 乗客の到着間隔をモデル化するため

■トラフィックの所要時間の予測 → 自動車の移動時間やトラフィックの所要時間を予測するため

■信号制御の最適化 → 信号のサイクルタイムやフェーズの最適化を行うため

■交通事故の発生確率の評価 → 交通事故の発生確率を評価するため

と、まあ、ここまでは分かったのですが、この分布の数学的な意義が分かりません。(正規分布や指数分布などは、理解できていると思います)

ですが、ラプラス分布の数学的な意義が分かりません。

で、以下のように推察しているのですが、私のこの推察は正しいでしょうか?

ラプラス分布を使う理由(江端の私見)

■外れ値のモデル化の為

→ そもそも、バスの到着時間が、正規分布のように曖昧に分散していたらとても困る。情報通信についても同様。とは言え、外れ値を完全に無視することはできない。そこで、このような、交通、情報を扱う分布として、(積分して1になる関数として)便利なツールとして、ラプラス分布が使われている

どなたか、ご見解を頂ければ幸いです。

江端

未分類

Golangで、以下のXML分の中から、<tag k="route" v="bus"/>を発見した時に、refの要素を参照にして位置情報を取り出すプログラムを書いて

<osm>
  <node id="2197214759" lat="35.5714149" lon="139.5880043" version="1" timestamp="2013-03-12T09:31:19Z" changeset="15336763" uid="1258339" user="k___0e0">
    <tag k="highway" v="bus_stop"/>
    <tag k="name" v="有馬第二団地前"/>
  </node>
  <node id="2197214762" lat="35.5715628" lon="139.5875429" version="1" timestamp="2013-03-12T09:31:19Z" changeset="15336763" uid="1258339" user="k___0e0">
    <tag k="highway" v="bus_stop"/>
    <tag k="name" v="有馬第二団地前"/>
  </node>
  <relation id="2814953" version="38" timestamp="2023-06-14T08:20:58Z" changeset="137316398" uid="1687166" user="U20">
   <member type="node" ref="2197214759" role="backward_stop"/>
    <member type="node" ref="2197214762" role="forward_stop"/>
    <tag k="route" v="bus"/>
  </relation>
</osm>

ChatGPTの回答

指定されたXMLデータから を検出し、それに対応する ref 要素を参照にして位置情報を取得するGolangのプログラムを以下に示します。このプログラムは、encoding/xml パッケージを使用してXMLを解析します。

package main

import (
	"encoding/xml"
	"fmt"
	"log"
	"strings"
)

type OSM struct {
	XMLName xml.Name `xml:"osm"`
	Nodes   []Node   `xml:"node"`
	Relations []Relation `xml:"relation"`
}

type Node struct {
	ID       int64   `xml:"id,attr"`
	Lat      float64 `xml:"lat,attr"`
	Lon      float64 `xml:"lon,attr"`
	Tags     []Tag   `xml:"tag"`
}

type Relation struct {
	ID       int64   `xml:"id,attr"`
	Members  []Member `xml:"member"`
	Tags     []Tag    `xml:"tag"`
}

type Member struct {
	Type string `xml:"type,attr"`
	Ref  string `xml:"ref,attr"`
	Role string `xml:"role,attr"`
}

type Tag struct {
	K string `xml:"k,attr"`
	V string `xml:"v,attr"`
}

func main() {
	xmlData := `
	<osm>
		<node id="2197214759" lat="35.5714149" lon="139.5880043" version="1" timestamp="2013-03-12T09:31:19Z" changeset="15336763" uid="1258339" user="k___0e0">
			<tag k="highway" v="bus_stop"/>
			<tag k="name" v="有馬第二団地前"/>
		</node>
		<node id="2197214762" lat="35.5715628" lon="139.5875429" version="1" timestamp="2013-03-12T09:31:19Z" changeset="15336763" uid="1258339" user="k___0e0">
			<tag k="highway" v="bus_stop"/>
			<tag k="name" v="有馬第二団地前"/>
		</node>
		<relation id="2814953" version="38" timestamp="2023-06-14T08:20:58Z" changeset="137316398" uid="1687166" user="U20">
			<member type="node" ref="2197214759" role="backward_stop"/>
			<member type="node" ref="2197214762" role="forward_stop"/>
			<tag k="route" v="bus"/>
		</relation>
	</osm>
	`

	var osmData OSM
	err := xml.Unmarshal([]byte(xmlData), &osmData)
	if err != nil {
		log.Fatal(err)
	}

	for _, relation := range osmData.Relations {
		for _, tag := range relation.Tags {
			if tag.K == "route" && tag.V == "bus" {
				for _, member := range relation.Members {
					if member.Type == "node" {
						node := getNodeByID(member.Ref, osmData.Nodes)
						if node != nil {
							fmt.Printf("Bus Stop: %s\n", getNodeName(node))
							fmt.Printf("Coordinates: Lat %f, Lon %f\n\n", node.Lat, node.Lon)
						}
					}
				}
			}
		}
	}
}

func getNodeByID(ref string, nodes []Node) *Node {
	for _, node := range nodes {
		if fmt.Sprintf("%d", node.ID) == ref {
			return &node
		}
	}
	return nil
}

func getNodeName(node *Node) string {
	for _, tag := range node.Tags {
		if tag.K == "name" {
			return tag.V
		}
	}
	return ""
}

このプログラムは、与えられたXMLデータから を見つけ、それに関連する ref 要素を参照してバス停の位置情報を取得して表示します。XMLデータは xmlData 変数に直接格納されていますが、実際のプログラムではファイルから読み込むこともできます。コード内のコメントに従って、XMLデータ構造やタグを調整してください。


世界が変わる

生成AIを使わない奴は、"死ぬ" ―― 文字通りの意味で。