2023,江端さんの忘備録

ここ1週間ほど、横浜市交通局バス関連リアルタイム情報の取得手段と、それを使ったビューアを作っていました。

「Protocol Buffersって何? 」から、「公共交通オープンデータ」を攻略する

For the past week or so, I have been working on a means of obtaining real-time information related to Yokohama City Transportation Bureau buses and a viewer that uses this information.

昨日1日で、基盤のコードを差し替えて、AWSにアップして、TLS対応して、最終的にサーバ化に成功しました。

Yesterday in one day, I replaced the code of the infrastructure, uploaded it to AWS, TLS support, and finally succeeded in making it a server.

しかし、私ですら、これがたった1日で完了するとは思っていなかったので、驚いています(大抵の場合、完工までは、見積の3~10倍になります)

But even I was surprised, as I did not expect this to be completed in just one day(In most cases, the estimated cost will be 3 to 10 times the estimated cost until completion).

どうして、こんなミラクルができたのか?

How was this miracle possible?

―― 過去の私が助けてくれたから

"Because my past helped me"

です。

-----

私、2020年5月に、自分のホームページのプラットフォームをWordPressに置き替えました。

I, in May 2020, replaced my website platform with WordPress.

で、そこに、ジャンル無視で、メモを書きまくり、無節操に公開してきました ―― よくご存知だとは思いますが。

And there I have been writing notes all over the place, ignoring genres, and publishing them in an uncontrolled manner -- as I'm sure you are well aware.

もちろん、そのメモは『私だけが分かれば良い』というものであって、前提条件や構築環境や指定するディレクトリなどは、全部無記載のままです。

Of course, the notes are 'only for me to know', and all the prerequisites, construction environment, and directories to be specified are left undocumented.

正直『私以外の他人が読んでも、まったく分からんだろう』と思います。

I honestly think, 'If someone else other than me reads it, they won't understand it at all.

しかし、書いてきた私にだけには『分かる』。

But only I, who have been writing, 'get it'.

これ、結構、重要です。

This is pretty important.

-----

このようなメモであれば、別にブログで公開しなくても良いと思いますよね。

If it is a memo like this, it doesn't needs to be published on a blog.

しかし「ブログ」にすると、『置き場所が分からなくなること』を防止できるのです。

However, "blogging" prevents 'misplacing'.

WordPressで一元管理すると、私がどこにいようが(会社、実家、大学、その他)、キーワード一発で、過去もメモを探し出すことができるのです。

When I centralize my notes in WordPress, no matter where I am (office, parents' house, university, etc.), I can find my past notes with a single keyword shot.

そして、WordPressは、コードを綺麗に表示したり、情報を貼り付けたりするのに、とても便利なのです。後日の追記も簡単です。

And WordPress is a great way to display code nicely and paste information. It is also easy to add later.

これに慣れてしまうと、メモ作りなんぞ、アホらしくてやってられません。

Once I get used to this, making memos is just plain stupid.

-----

もちろん、私のプライベートに関わるものもあるのですが、そのような情報は非公開にしています。

Of course, some of the information concerns my personal life, but I keep such information private.

ところが、WordPressは、この「非公開機能」が、あまり当てにならない。

WordPress, however, is not so trustworthy with this "private" feature.

で、今日、セキュリティ上の深刻な問題点を見つけてしまったので、大慌てて対応していました(本日の4時間を持っていかれてしまいました。まだ完了していません)。

So, today I found a serious security issue and was in a big hurry to deal with it (it took 4 hours of my day. It has not been completed yet).

-----

私、分からないことがあると、Googleよりも、自分のブログで検索をします。

When I don't know something, I search on my blog rather than Google.

相当高い確率で、私のブログは、私の疑問に応えてくれます。

At a fairly high rate, my blog answers my questions.

私自身は、『情報発信』なんぞは比較的どーでもよく、『過去の私に助けて貰う』という視点からブログを使い倒しています。

I myself am relatively unconcerned about "information dissemination" and use blogs from the perspective of "getting help from the past me.

そのツールとしてWordPressはお勧めです。

I recommend WordPress as a tool for this purpose.

まあ、私、ブログのプラットフォームとしては、ベタベタなhtmlとWordpressしか知らないので、比較のしようがありませんが。

Well, I only know sticky html and WordPress as blogging platforms, so I have no way to compare them.

未分類

このコンテンツはパスワードで保護されています。閲覧するには以下にパスワードを入力してください。

2023,江端さんの技術メモ

公開回、秘密鍵の対応

log.Fatal(http.ListenAndServeTLS(*addr, "./cert.pem", "./key.pem", nil)) // localhost:8080で起動をセット

if httpErr = http.ListenAndServeTLS(*addr, "./fullchain.pem", "./privkey.pem", nil);

ということで、 cert.pem = fullchain.pem  key.pem = privkey.pem で良いのだろう

Let's encrypt を試してみた件(整理は明日)

2023,江端さんの忘備録

今、ガリガリと、英語のカンファレンスペーパーを書いています。

I'm scribbling away and writing a conference paper in English.

(私の執筆スタイルについては、こちらをどうぞ)

(For more information on my writing style, please click here.)

翻訳エンジンがあるからできることです。

This is possible because of the translation engine.

-----

私、一度、英文チェックの外注を通してから論文を学会に投げたら、学会のレビューアーから、

Once I pitched a paper to a conference after going through an outsourced English language check, from a reviewer at the conference, I was given the following message.

『英文がなっていない』

"Your English sentence is not good enough"

というレビューが返ってきたことがあります。

その件を、外注先にクレームとして報告したら、慌てて担当者を交代させてきました。

When I reported the matter to the subcontractor as a complaint, they rushed to replace the person in charge.

この外注先、いつもエラそうに、私の英語の品質に最低の評価結果を付けて戻してくるので(そんな、いらんこと、せんでもいいのに(多分、うちの会社が頼んでいるんだろうが))、久々に胸のすく思いがしました。

This subcontractor, who always comes back with the lowest rating on the quality of my English (even though they don't have to do that (maybe my company is asking them to do it)). Anyway I had felt happy for long time.

しかし、その後、私は、外注チェックの依頼をしなくなりました。

After that, however, I stopped asking for outsourced checks.

腹が立ってきたからです。

I was getting angry at the conference.

『私の英文が気にいらないなら、それならそれで構わん。それを理由にリジェクト(却下)してもらって結構だ』

"If you don't like my English, that's fine, and you can reject it for that reason."

という気持ちになりました。

I felt that.

私的な見解ですが ―― 現在のアカデミズムに決定的に欠けているのは『思いやり』なんじゃないかなぁ、と思うのです。

-----

確かに私たちノンネイティブが書く英語は、ネイティブから見れば、不自然なものかもしれません。

It is true that the English we non-native English speakers write may be unnatural from the perspective of native speakers.

勿論『意味が通じない』というのであれば、批判されて仕方がないと思いますが、『不自然』であるというコメントには、納得できません。

Of course, if it 'doesn't make sense', then I guess I can't help but be criticized, but I don't agree with the comment that it is 'unnatural'.

こちらにも書いていますが、私は、英語の種類は、世界の国の数だけ、あるいは世界の人の数だけある、と思っています。

As I wrote here, I believe that there are as many varieties of English as there are countries in the world, or as many people in the world.

 

『自分がラクして読めない英文を、寛容に受けいれることが、インターナショナルであり、多様性だろうが』と思うのです。

I think, 'Tolerance and acceptance of English texts that you cannot read with ease is what makes us international and diverse'.

-----

別の方向からも考えてみました。

I thought about it from another direction.

最近、「異世界ファンタジー」流行っているじゃないですか(流行っているんです)。

Recently, "otherworldly fantasy" has become popular (it is so).

で、ここで、『世界共通語が日本語であり、アカデミズムの世界では、日本語が研究分野の共通言語である』という異世界を想定します ―― ファンタジーの要素はなさそうですが。

So, here we assume an alternate world where 'the universal language is Japanese, and in the world of academia, Japanese is the common language of research fields' -- although there does not seem to be an element of fantasy.

そこに、漢字が誤記だらけで、"てにをは"が滅茶苦茶、関係代名詞"that"をそのまま「それは」とか翻訳したような文が提出されたとしたら、

If a sentence was submitted that was full of kanji errors, "te ni wo ha" was a mess, and the relative pronoun "that" was translated as "that",

『さすがに、これは読みにくいだろうなぁ』

'As expected, this will be hard to read'

とは予想できます。

そして、レビューアーの私は、

And I, as the reviewer, might responce the following,

『日文がなっていない』

"Your Japanese sentence is not good enough."

と返事をするかもしれんなぁ、と。

-----

あれ? というとは、私の論文の英文は、『そういう感じの英文』だったということ?

Huh? Does that mean that the English sentence of my paper was 'that kind of English sentence'?

と、今、いきなり弱気になっています。

And now I am suddenly feeling vulnerable.

2023,江端さんの忘備録

シニアは「窓際」というイメージがあります。

Seniors workers have an image of being "window-dressers".

『楽しい「追い出し部屋」ライフ』という観点からも、プログラミング技術の取得はお勧めです。

しかし、今の私は、窓の外にいます ―― 「屋外の工事現場」です。

But now I am outside the window -- an "outdoor construction site".

一人の労働者として、道路工事や建設現場でがんばっている高齢の職人さん達をみると、肩を組んで、歌いながら、一緒にお酒を飲みたい、という気持ちになります ―― 本当です。(私は、今、断酒中(4年目)ですが)。

As a worker, when I see elderly craftsmen working hard at road and building construction sites, I want to rub shoulders with them, sing with them, and have a drink with them -- really. (I am currently in my fourth year of sobriety).

-----

私の今の仕事を端的にいうと、「(有)江端ソフトウェア開発」という会社の個人事業者です。

To put my current job in a nutshell, I am the sole proprietor of a company called "Ebata Software Development, Inc."
(*)新会社法施行後「有限会社」は消滅していますが、ここでは、『有限責任』の概念として使用します。

但し、(1)発注仕様書を自分で作り、(2)納期を自分で決定し、(3)完工後のメンテナンスは自分の気分次第、という、他のソフトウェアの外注会社から見れば『激怒される』ような内容ですが。

However, at this company, (1) I make the order specifications myself, (2) I decide the delivery date myself, and (3) maintenance after completion depends on my own mood. From the perspective of other software outsourcing companies, this is the kind of work that would make them "furious.

まあ、だから「(有)江端ソフトウェア開発」であって、「(株)江端ソフトウェア開発」ではないのです。

Therefore, the company is not ordinary one.

-----

この状況は、深刻な人手不足がもたらしているのかもしれません。

This situation may be brought about by a severe shortage of labor.

新入社員の段階で、『上流工程』だの『コンセプト』だのと言うことはできても、モノを作った経験がないエンジニアが大量に送り込まれてくるからです。

This is because a large number of engineers are sent in at the new hire stage, who can talk about "upstream processes" and "concepts," but have no experience in creating things.

だから、私は、「昔取った杵柄(きねづか)」を、手放すことができないのです ―― 今や、私の「杵柄」はボロボロです。

That's why I can't let go of my "kinezuka" -- my "kinezuka" is now in tatters.

正直、私は『シニアになれば、新しい技術を使い倒す若者によって居場所がなくなる』と信じていました。

Frankly, I believed that 'when I become a senior, I will be displaced by young people who will use up all the new technology.

ところが、蓋を開けてみれば、道路工事や建設現場やシステム開発だけでなく、日本全体が「シニア」をアテにしているという状況(惨状)です。

However, when we open the lid, we find that not only construction sites and system development, but also the whole of Japan is relying on "seniors" (a disastrous situation).

-----

まあ、これ「彼ら」が悪いというよりも、「雇用主(会社)」が悪い。

Well, this is not so much "their (the newcomer's)" fault, but rather "the employer's (the company's)" fault.

趣味でも勉強でもバイトでも構いませんが、自力でシステムを組み上げたこともない人が、どうやってモノを作れるのか、私は今でも分からなのですが ―― 会社の募集要項に、確かに書かれているんですよね。

It doesn't matter if it's a hobby, study, or part-time job, I still don't know how a guy who has never put together a system on his own can build things.However the application guidelines certainly say that.

『プログラミング経験は、採用の要件ではない』、と。

Programming experience is not a requirement for employment."

確かに、プログラミングだけができても駄目ですが、プログラミングの経験すらない人が、システムを語れるのか、私には甚だしく疑問なのです。

Well, it is true that it is no good if one can only program, but I highly doubt that a person without even programming experience can talk about systems.

私は、過度な現場主義を嫌悪していますが、一人で、スクラッチからシステム(Webページくらいでも良い)を組み上げる経験をしたことをない人間を、モノ作りの仲間として認めるのは、大変難しいです。

I detest excessive fieldwork, but it is very difficult for me to accept a person who has never built a system (or even a web page) from scratch by themselves as a member of the manufacturing community.

-----

では、江端は入社時にプログラミングができていたのか?

So, was Ebata able to program when he joined the company?

実際のところ、入社時の私のプログラミングは、N80-BASICクラスの「動いているだけ」という程度のものでした。

In fact, my programming skills at the time I joined the company were about the N80-BASIC level, so called "just working".

本格的なコーディングは、入社後だったと思います ―― なので、入社時の私のスキルは、今の若い人と同じです。

I think the real coding started after I joined the company -- so my skills at the time I joined were the same as a young person today.

後は、個人的なイデオロギーの違いがあるだけです。

The rest is just personal ideological differences.

『"動いていない"は、"無い"と同じである』と、私は信じていますので。

I believe that "not moving" is the same as "not existing".

-----

とは言え、「(有)江端ソフトウェア開発」は、プログラミングだけしていればよい、という会社ではありません。

However, "Ebata Software Development" is not a company that only needs to do programming.

20ページのプレゼンテーション資料を1時間で作ったり、1日で研究報告書を執筆しろと言われたり、ソフト外注先のプログラムの動作不良を潰すことを頼まれたり、無茶な学会発表を命じられたり、予算の計算したり、各種の発注作業をしたり、大学の授業を受けたり、講義したり ――

The company was asked to create a 20-page presentation in an hour, write a research report in a day, crush a software subcontractor's program malfunction, present at a conference, calculate budgets, place various orders, take university classes, give lectures, and so on. --

それでも、

Howeve, it is

―― 『動かせ』と言われたら、"No"と言わない(言えない)会社

"The company that does't or cannot say "No", whenever asked to move something"

です。

『自作のプログラミングコード一本で、研究原資という"タマ"を取りに行く』

2023,江端さんの忘備録

コーディング(プログラミング)は、バグの発生理由やAPI仕様が分からくなって、結構な頻度で「ハマる」ことがあります。

Coding (programming) can get "stuck" quite often, due to bugs or confusion about API specifications.

私のやっているような、専門(カルト)分野になりますと、それについての説明記事すら見当たらない、という状況になります。

Specialized (cult-like) field like the one I work in, I can't even find an article explaining it.

まあ、だからこそ、私は、自分のハマった内容は、それを『キレイに纏めないで』ブログで公開しています。

Well, that's why I publish what I have faced the problem on my blog, without 'neatly summarizing' it.

私のゴミのようなメモでさえ、何の情報もないよりは、はるかにマシなのです。

Even my garbage notes are far better than no information whatsoever.

-----

さて、このコーディングの「ハマる」問題は、その対応のためにムキになって、酷い時には、2~3日の時間を丸々持っていかれたあげく成果ゼロ、ということもあります。

Now, this coding "getting stuck" issue is one that I get really pissed off to deal with. In the worst case, it can take up two or three full days of my time, with zero results.

このコーディングの沼から抜け出すためには、「打ち切る」という勇気が必要です。

To get out of this coding quagmire, we need to have the courage to "abort" it.

ところが、この「打ち切る」というのが、本当に難しいのです。

However, this "abort" is really difficult.

『サンクコストの呪い』というやつです。

It's called "The Curse of Sunk Costs."

―― エジソン、バカ

-----

こういう場合、『有識者の方にメールで質問』をしています。

In cases like this, I 'email questions to the experts'.

こうして、その問題から一時離れます。

Thus, I temporarily step away from the problem.

もちろん、これは、相手に迷惑をかけるシャレにならない方式ですし、そもそも失礼です。

Of course, this is a shameless method that inconveniences others and is rude in the first place.

ですので、普段から相手との信頼関係を構築しておかなければなりません。

Therefore, I must build a relationship of trust with them on a regular basis.

その信頼関係とは何かと言えば ―― その方からの質問に対しては『自分の持っている全ての知見を、最速かつ無条件で開示する』ということです。

What is that relationship of trust? When that person asks me a question, I have to 'disclose all the knowledge I have in the fastest and unconditional way possible".

これを、日常的に行っていなければなりません。

This must be done on a daily basis.

これを「人脈」といいます。

This is called "human networking".

名刺を交換することは、「人脈」とは言いません。

Exchanging business cards is not "networking".

人脈には、ちゃんと情報交換と信頼関係という「血液」を流し続けなければならないのです。

We must keep the "blood" of proper exchange of information and trust flowing through our connections.

「血液」が流れていない「人脈」は、ただの「管」です。

A "networking" without "blood" is just a "tube.

-----

逆に言うと ―― 私は、思い出したように、困った時だけ私を使おうとする奴に対しては、意図的に(悪意的に)対応しません。

Conversely -- I will not intentionally (maliciously) respond to anyone who tries to use me only when they are in trouble, as I recall.

私の情報提供に対して、礼の一つも寄越してこないような人間に対しては、「管」そのものを切断します。

I have cut off the "tube" itself to anyone who does not send me even a single thanks mail for the information I provide.

まあ、このような狭量の人間を『大人げない』というのでしょうが ――

Well, I guess you could call such a narrow-minded person 'childish', however,

私の知る限り、世の中の大半の大人は、『大人げない』です。

As far as I know, most adults in the world are not 'childish'.

私は、過去は振り向かない人間です ―― 特に、他人の過去は。

2023,江端さんの技術メモ

T研究所のKさん。ありがとうございました。

 

(4)PruneClusterにおいて、クラスタリングを解除する方法 → leafletView の定義の後に、「leafletView.Cluster.Size = -1」のよう     に負の値を設定することによって、実現可能です。     - 参考URL:https://github.com/SINTEF-9012/PruneCluster/issues/52

2023,江端さんの忘備録

現在、横浜市交通局さんから提供して頂いている情報を使って、バスのリアルタイム情報を表示させる作業をしています。

I am currently working on displaying real-time bus information using information provided by the Yokohama City Transportation Bureau.

「Protocol Buffersって何? 」から、「公共交通オープンデータ」を攻略する

昨夜、コーディングを再開したら、交通データが全く表示されなくなっていました。

When I resumed coding last night, the traffic data was not showing up at all.

真っ青になって、プログラムを見直してみたのですが、障害の原因となるような箇所が見つかりません。

In a panic, I reviewed the program, however I could not find anything that could cause the failure.

散々調べた挙げく、妙な短文データが表示されているので、これをデコードして、ようやく原因が分かりました。

After much investigation, I finally found the cause of the problem by decoding the strange short text data being displayed.

―― 深夜は路線バスは走っていません

"No buses running late at nigh"

-----

まあ、バスが走っていない時間にコーディングしている私が悪いんですけどね。

Well, it is my fault for coding at a time when buses are not running.

それにしても、こんなしょーもない理由で、大切な深夜の2時間がふっとんだかと思うと、なんともいまいましいです。

Still, it's a damn shame to think that two precious late-night hours were lost for such a trivial reason.

2023,江端さんの技術メモ

バスのリアルタイム情報を格納するサーバを作ることになりました(経緯が色々あって)。

情報を提供しているサーバから、JSONを定期的に取りに行けばいいんだろう、とか思っていたのですが、このProtocol Bufferというデータ形式を、私は聞いたことがありません。

実際にデータを取得してセーブしてみたのですが、明らかにバイナリです。

しかも、どのエディタでも自動整形しない。ということは、どうやらテキストではない。

で、色々調べたのですが、どうも要領を得ないのですが、下の動画でやっと分かった気になりました。

乱暴に言えば、Protocol Buffersとは「圧縮されたJSON または XML(のようなもの)」です。

まあ、JSONもXMLもテキストメッセージで、しかも構造を保持したまま送信するので「通信効率悪そうだなぁ」と前々から思っていましたので、こういうものが必要となるのは分かります。

Googleが提供していることもあり、また、リアルタイム系のデータでは必要となるのは分かりますが ―― また、覚えることが増えたなぁ、と少々ウンザリしています。


とりあえず、Go言語で動かすことを目的として、ここまでの経緯を記載しておきます。

私の環境は、Windows10です。GOはインストール済みです

(1)https://protobuf.dev/downloads/ から "Latest Version" →  "*-win64.zip"

をダウンロード。私はC:\の直下に展開しました。

でもって、この両方のフォルダーにpathを通しておきました。必要なのは"protoc.exe"です。

(2)私の場合、C:\Users\ebata\protocol_bufferesというディレクトリを作って、そこにソースを展開することにしました。

とりあえず、main.goという名前でファイルを作っておきます(後で必要になります)

package main

import "fmt"

func main() {
	fmt.Println("Hello World!!")
}

さらに、person.proto という名前で、

syntax = "proto3";
package main;

message Person{
    string name = 1;
    int32 age = 2;
}

というファイルを作ります。これがxmlやらJSONのスタイルファイルにようなものです。

で、ここから、person.pb.goというファイルを作らなければならないのですが、これに苦労しました。

C:\Users\ebata\protocol_bufferesの中で、

$ protoc --go_out=. *.proto
protoc-gen-go: unable to determine Go import path for "person.proto"
Please specify either:
    • a "go_package" option in the .proto source file, or
      • a "M" argument on the command line.
See https://developers.google.com/protocol-buffers/docs/reference/go-generated#p
ackage for more information.
--go_out: protoc-gen-go: Plugin failed with status code 1.

のようなことを繰り返していたのですが、person.proto の中に、一行追加したら、サクっと通りました。

syntax = "proto3";

option go_package="./;main"; // 理由は分からないけど、この1行で、以下のエラーが消えた

//$ protoc --go_out=. *.proto
//protoc-gen-go: unable to determine Go import path for "person.proto"
//Please specify either:
//        • a "go_package" option in the .proto source file, or
//        • a "M" argument on the command line.
//See https://developers.google.com/protocol-buffers/docs/reference/go-generated#p
//ackage for more information.
//--go_out: protoc-gen-go: Plugin failed with status code 1.

package main;

message Person{
    string name = 1;
    int32 age = 2;
}

この結果、以下のようなperson.pb.goが生成されました。

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.26.0
// 	protoc        v4.22.2
// source: person.proto

package main

import (
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
	reflect "reflect"
	sync "sync"
)

const (
	// Verify that this generated code is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
	// Verify that runtime/protoimpl is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

type Person struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	Age  int32  `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
}

func (x *Person) Reset() {
	*x = Person{}
	if protoimpl.UnsafeEnabled {
		mi := &file_person_proto_msgTypes[0]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *Person) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*Person) ProtoMessage() {}

func (x *Person) ProtoReflect() protoreflect.Message {
	mi := &file_person_proto_msgTypes[0]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use Person.ProtoReflect.Descriptor instead.
func (*Person) Descriptor() ([]byte, []int) {
	return file_person_proto_rawDescGZIP(), []int{0}
}

func (x *Person) GetName() string {
	if x != nil {
		return x.Name
	}
	return ""
}

func (x *Person) GetAge() int32 {
	if x != nil {
		return x.Age
	}
	return 0
}

var File_person_proto protoreflect.FileDescriptor

var file_person_proto_rawDesc = []byte{
	0x0a, 0x0c, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04,
	0x6d, 0x61, 0x69, 0x6e, 0x22, 0x2e, 0x0a, 0x06, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x12, 0x12,
	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
	0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,
	0x03, 0x61, 0x67, 0x65, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62,
	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}

var (
	file_person_proto_rawDescOnce sync.Once
	file_person_proto_rawDescData = file_person_proto_rawDesc
)

func file_person_proto_rawDescGZIP() []byte {
	file_person_proto_rawDescOnce.Do(func() {
		file_person_proto_rawDescData = protoimpl.X.CompressGZIP(file_person_proto_rawDescData)
	})
	return file_person_proto_rawDescData
}

var file_person_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_person_proto_goTypes = []interface{}{
	(*Person)(nil), // 0: main.Person
}
var file_person_proto_depIdxs = []int32{
	0, // [0:0] is the sub-list for method output_type
	0, // [0:0] is the sub-list for method input_type
	0, // [0:0] is the sub-list for extension type_name
	0, // [0:0] is the sub-list for extension extendee
	0, // [0:0] is the sub-list for field type_name
}

func init() { file_person_proto_init() }
func file_person_proto_init() {
	if File_person_proto != nil {
		return
	}
	if !protoimpl.UnsafeEnabled {
		file_person_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Person); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
	}
	type x struct{}
	out := protoimpl.TypeBuilder{
		File: protoimpl.DescBuilder{
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
			RawDescriptor: file_person_proto_rawDesc,
			NumEnums:      0,
			NumMessages:   1,
			NumExtensions: 0,
			NumServices:   0,
		},
		GoTypes:           file_person_proto_goTypes,
		DependencyIndexes: file_person_proto_depIdxs,
		MessageInfos:      file_person_proto_msgTypes,
	}.Build()
	File_person_proto = out.File
	file_person_proto_rawDesc = nil
	file_person_proto_goTypes = nil
	file_person_proto_depIdxs = nil
}

さて、ここで、

package main

import (
	"fmt"
	"log"

	"google.golang.org/protobuf/proto"
)

func main() {
	fmt.Println("Hello World!!")

	elliot := &Person{
		Name: "Elliot",
		Age:  24,
	}

	data, err := proto.Marshal(elliot)
	if err != nil {
		log.Fatal("Marshalling error", err)
	}

	fmt.Println(data)
}

とした上で、

$ go run main.go person.pb.go

をすると、

main.go:7:2: no required module provides package google.golang.org/protobuf/prot
o: go.mod file not found in current directory or any parent directory; see 'go h
elp modules'
person.pb.go:10:2: no required module provides package google.golang.org/protobu
f/reflect/protoreflect: go.mod file not found in current directory or any parent
directory; see 'go help modules'
person.pb.go:11:2: no required module provides package google.golang.org/protobu
f/runtime/protoimpl: go.mod file not found in current directory or any parent di
rectory; see 'go help modules'

というエラーがでてくるので、
$go mod init m
$ go get google.golang.org/protobuf/reflect/protoreflect
$ go get google.golang.org/protobuf/proto
$ go get google.golang.org/protobuf/runtime/protoimpl

を実行して、再度、

$ go run main.go person.pb.go

を行うと

Hello World!!
[10 6 69 108 108 105 111 116 16 24]

とい値が出力されます。

package main

import (
	"fmt"
	"log"

	"google.golang.org/protobuf/proto"
)

func main() {
	fmt.Println("Hello World!!")

	elliot := &Person{
		Name: "Elliot",
		Age:  24,
	}

	data, err := proto.Marshal(elliot)
	if err != nil {
		log.Fatal("Marshalling error", err)
	}

	fmt.Println(data)

	newElliot := &Person{}
	err = proto.Unmarshal(data, newElliot)
	if err != nil {
		log.Fatal("unmarshalling error: ", err)
	}

	fmt.Println(newElliot.GetAge())
	fmt.Println(newElliot.GetName())

}

のプログラムを実行すると、

$ go run main.go person.pb.go
Hello World!!
[10 6 69 108 108 105 111 116 16 24]
24
Elliot

となる。


動的バス情報フォーマット(GTFSリアルタイム)ガイドライン

やっと見つけた

ここからダウンロードすると、こんな感じのprotoファイルが得られます。

で、これを以下のように修正して、

として、

$ protoc --go_out=. *.proto

を実施すると、さくっとgtfs-realtime.pb.goができました。

ここまでできれば、後はクライアントを作れば、できるはずです。

問題は、どうやってサーバにアクセスするか、を考えれば、いくはずです。
(が、基本的に最後に動くまで、どうなるかは分からないですが)。


さて、今回の私の狙いは、

https://ckan.odpt.org/dataset/b_bus_gtfs_rt-yokohamamunicipal

の、リアルタイムデータを取得して保存しておくことです。

これは、こちらに記載されているように

URL: https://api.odpt.org/api/v4/gtfs/realtime/YokohamaMunicipalBus_vehicle?acl:consumerKey=[発行されたアクセストークン/YOUR_ACCESS_TOKEN]

なので、このデータを所得するためには、「発行されたアクセストークン」というのを貰う必要があります。これ、"f4954c3814b207512d8fe4bf10f79f0dc44050f1654f5781dc94c4991a574bf4"(江端ルールで変更済み)というやつになります。

これは、https://developer.odpt.org/ から、申請してメールで付与してもらえます(2日くらいかな)。これがないと、データの取得ができないので注意して下さい。

さて、ここから、とりあえず、この横浜市交通局の市営バスのバス関連リアルタイム情報を取得するコードを、https://gtfs.org/realtime/language-bindings/golang/ をパクって、ちょっと修正してみました。

私は、c:\Users\ebata\protocol_bufferes\gtfs-realtime というディレクトリを掘って、上記のページ通りのことを実施しました。

$ go get github.com/MobilityData/gtfs-realtime-bindings/golang/gtfs

$ go get google.golang.org/protobuf/proto

以下のファイルを作成しました(トークンは自分のものに置き替えて下さい)。エラーの出てくるところは、コメントアウトしています。

// https://gtfs.org/realtime/language-bindings/golang/

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	"github.com/MobilityData/gtfs-realtime-bindings/golang/gtfs"
	proto "github.com/golang/protobuf/proto"
)

func main() {
	var (
		username = "xx@gmail.com" // 横浜市交通局の市営バスのサイトでは不要のようだからダミーを放り込んでおく
		password = "xx"           // (同上)
	)

	client := &http.Client{}
	req, err := http.NewRequest("GET", "https://api.odpt.org/api/v4/gtfs/realtime/YokohamaMunicipalBus_vehicle?acl:consumerKey=f4954c3814b207512d8fe4bf10f79f0dc44050f1654f5781dc94c4991a574bf4", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.SetBasicAuth(username, password)
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}

	defer resp.Body.Close()
	if err != nil {
		log.Fatal(err)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(body)

	feed := gtfs.FeedMessage{}
	err = proto.Unmarshal(body, &feed)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(feed)

	/*

		for _, entity := range feed.Entity {
			tripUpdate := entity.TripUpdate
			trip := tripUpdate.Trip
			tripId := trip.TripId
			fmt.Printf("Trip ID: %s\n", *tripId)
		}
	*/
}

で、fmt.Println(body) のところを表示するとこんな感じになっています。

[10 13 10 3 50 46 48 16 0 24 165 246 160 161 6 18 41 10 9 118 105 99 108 95 49 56 48 54 34 28 18 10 13 252 214 13 66 21 192 155 11 67 40 165 205 159 161 6 66 6 10 4 49 56 48 54 72 0 18 41 10 9 118 105 99 108 95 49 48 48 50 34 28 18 10 13 188 173 13 66 21 205 159 11 67 40 233 176 159 161 6 66 6 10 4 49 48 48 50 72 0 18 41 10 9 118 105 99 108 95 51 57 57 50 34 28 18 10 13 83 131 13 66 21 28 146 11 67 40 216 210 159 161 6 66 6 10 4 51 57 57 .....

fmt.Println(feed)は、こんな風に表示されます。

{{{} [] [] 0xc00013f800} 0 [] map[] gtfs_realtime_version:"2.0" incrementality:FULL_DATASET timestamp:1680358181 [id:"vicl_1806" vehicle:{vehicle:{id:"1806"} position:{latitude:35.459946 longitude:139.6084} timestamp:1680336549 occupancy_status:EMPTY} id:"vicl_1002" vehicle:{vehicle:{id:"1002"} position:{latitude:35.419662 longitude:139.62422} timestamp:1680332905 occupancy_status:EMPTY} id:"vicl_3992" vehicle:{vehicle:{id:"3992"} position:{latitude:35.378246 longitude:139.57074} timestamp:1680337240 occupancy_status:EMPTY} id:"vicl_1732" vehicle:{trip:{trip_id:"04117_12202301042041P01910" schedule_relationship:SCHEDULED} vehicle:{id:"1732"}.....

あとは、ここをパースすれば、必要な情報は取り出せるはずです。


さて、ここから ~/protocol_bufferes/gtfs-realtime に環境を作ってみます。

$go mod init m
$ go get google.golang.org/protobuf/reflect/protoreflect
$ go get google.golang.org/protobuf/proto
$ go get google.golang.org/protobuf/runtime/protoimpl

で、

こちらの環境でも、上記と同じ手続で、gtfs-realtime.pb.goを作り、

$ go run main.go gtfs-realtime.pb.go

を実施してみましたところ、

main.go:11:2: no required module provides package github.com/MobilityData/gtfs-realtime-bindings/golang/gtfs; to
add it:
go get github.com/MobilityData/gtfs-realtime-bindings/golang/gtfs
main.go:12:2: missing go.sum entry for module providing package github.com/golang/protobuf/proto; to add:
go mod download github.com/golang/protobuf

と言われたので、言われた通りのことを実施してみました

ebata@DESKTOP-P6KREM0 MINGW64 ~/protocol_bufferes/gtfs-realtime
$ go get github.com/MobilityData/gtfs-realtime-bindings/golang/gtfs
go get: added github.com/MobilityData/gtfs-realtime-bindings/golang/gtfs v1.0.0
でもって、
ebata@DESKTOP-P6KREM0 MINGW64 ~/protocol_bufferes/gtfs-realtime
$ go mod download github.com/golang/protobuf
再度やってみました。
$ go run main.go gtfs-realtime.pb.go
go: updates to go.mod needed; to update it:
        go mod tidy
ここも言われた通り、
$ go mod tidy
をやりました。
でもって、また繰り返しました。
$ go run main.go gtfs-realtime.pb.go
panic: proto: file "gtfs-realtime.proto" is already registered
        previously from: "github.com/MobilityData/gtfs-realtime-bindings/golang/gtfs"
        currently from:  "main"
See https://protobuf.dev/reference/go/faq#namespace-conflict
goroutine 1 [running]:
google.golang.org/protobuf/reflect/protoregistry.glob..func1({0xea9240, 0xeb92b8}, {0xea9240, 0xc000058d70})
        c:/go/pkg/mod/google.golang.org/protobuf@v1.30.0/reflect/protoregistry/registry.go:56 +0x1f4
(以下、色々)
エラー文を見てみると、"gtfs-realtime.proto"が既に登録されている、とあります。
まあ、 ~/protocol_bufferes/ で、色々やったので、多分、登録してしまったのだろうと思います。
が、それを色々変えるのは面倒ですので、とりあえず対処方として、環境変数を変えてしまう、という手があるようです。
$ export GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn
で、ここの部分は回避できるようです(本格的な修正は後回しとします)。
とりあえず、エラーが出てこないように、main.goでコメントしていた部分を解いて、エラーが出てこないように修正しました。
feed := gtfs.FeedMessage{}
	err = proto.Unmarshal(body, &feed)
	if err != nil {
		log.Fatal(err)
	}
	//fmt.Println(feed)

	for _, entity := range feed.Entity {
		//tripUpdate := entity.TripUpdate
		fmt.Println(entity)
		fmt.Println()
		//trip := tripUpdate.Trip
		//tripId := trip.TripId
		//fmt.Printf("Trip ID: %s\n", *tripId)
	}
とりあえずentity(が、何かよく分からんのですが)を表示してみました。
この結果、以下のような表示が得られました。
id:"vicl_1780"  vehicle:{trip:{trip_id:"05406_09202304031054P00218"  schedule_relationship:SCHEDULED}  vehicle:{id:"1780"}  position:{latitude:35.415165  longitude:139.66798}  current_stop_sequence:10  stop_id:"5899_02"  current_status:IN_TRANSIT_TO  timestamp:1680534894  occupancy_status:EMPTY}
id:"vicl_3944"  vehicle:{trip:{trip_id:"03903_22202304031012A01309"  schedule_relationship:SCHEDULED}  vehicle:{id:"3944"}  position:{latitude:35.50719  longitude:139.55861}  current_stop_sequence:32  stop_id:"6219_01"  current_status:IN_TRANSIT_TO  timestamp:1680528244  occupancy_status:EMPTY}
id:"vicl_1705"  vehicle:{vehicle:{id:"1705"}  position:{latitude:35.378166  longitude:139.57071}  timestamp:1680522432  occupancy_status:FEW_SEATS_AVAILABLE}
id:"vicl_4607"  vehicle:{vehicle:{id:"4607"}  position:{latitude:35.492794  longitude:139.66455}  timestamp:1680526330  occupancy_status:NOT_ACCEPTING_PASSENGERS}
さて、次は、この内容を理解しなければなりません。
とりあえず、バスのID、緯度、経度が取れればよく、これでデータが取れるようです.
for _, entity := range feed.Entity {
			//tripUpdate := entity.TripUpdate
			//fmt.Println(entity)

			// データの読み込み
			uniName := *(entity.Vehicle.Vehicle.Id)
			lat := *(entity.Vehicle.Position.Latitude)
			lon := *(entity.Vehicle.Position.Longitude)

			fmt.Println(uniName, lat, lon)
}

2023,江端さんの忘備録

今夜、異動先の部署のキックオフの飲み会に参加してきました。

Tonight I attended the kick-off drinks for the department I am transferring to.

こんな私でも、節目の行事は、大切だと思っています。

『なんだかんだいって、セクショナリズムの対抗手段は、定期的な飲み会である』

Even I would like to cherish milestone events.

お開きのところで、挨拶を求められたので、一言だけ申し上げてきました。

At the close of the meeting, I was asked to say a few words.

「本日、聞いたお話は、3年間は『書きません』ので、ご安心下さい」

"I will not write about what I heard today for the next three years"

-----

一瞬、『この人、何を言っているの?』という風の顔の後で、「その"話"とは何だ?」と一斉につっこまれましたが、私はにこやかな笑顔を見せたまま、そのまま散会しました。

Just after everyone looked like, "What is he talking about?, they asked me "What is this 'talk'?" immidately. However, Smiling at them and I left soon.

今後は、節目の行事すら、呼ばれなくなるかもしれません。

In the future, I might not be called even milestone events.