FareMap3 運賃一覧表示

FareMap更新。
・同一名の乗換駅はひとまとめにして表示するようにした。
・運賃一覧表示に対応した。駅をダブルクリックすると他の全駅への運賃が表示される。計算はほぼ正しいようだ。
・駅、駅名をドラッグできるようにした。保存はまだできない。

残り作業
・路線図の保存、読み込み、駅の大きさなどの変更
・同一駅間に複数の路線がある場合は線をずらして表示する
・計算の進捗を表示したい
・路線追加
・速度増加


田舎の欠点

昨日、腹立たしい出来事があった。
2月から下の子2人がある書道会の教室に通っていた。もう引っ越すので昨日が最後のレッスンとなった。この教室は、この界隈では有名なとある書家が会長の書道会で、西湘地域ではそこそこ大きな会だった。こどもたちを見てくれる先生はご高齢の女性で、その会長の奥さんだった。
月に1度会報が発行され、級位・段位別の順位が掲載される。その会報によると、どうも会長はごく最近に亡くなったらしく、会長は別の人に変わっていた。
最後のレッスンのとき、先生が「ちょっとひどいわよねぇ」とこどもたちに話しかけた。なんと、一番下の子の順位が不当に下げられたというのだ。先の新会長が「これは大人が手伝って書いたに違いない」と言いがかりをつけたという。もちろんそんなはずはない。先生も抗議したという。
手前味噌だが一番下の子の字は書道を初めて3か月なのに上手い。今回の課題は「は」だったのだが、見事な太く力強い字なのだ。その字が、何故か下から数えたほうが早い順位に落とされている。一番になった人の字は会報に写真で掲載される。のだが、その「は」は下手だった。曲がっているし、細くひょろひょろだった。ちなみに会長の娘はなぜかいつも1位で、必ず写真が掲載されている。今回の字は一人だけかすれていて、決して上手ではない。
こんなことがあると、田舎がますます嫌になる。異質なもの、脅威と感じたものをすぐに排除する。曇りの無い目で見ようとしない。先生はいい人だったのに、最後の最後でケチがついてしまった。
妻がこの気持ちを最も表現できる言葉がなにか探していた。
「バカヤロウ」
この5年間数々の習い事をこどもにさせてきたが、どれも見学だけでやめてしまったり、長続きせずすぐやめてしまっていた。いずれも理由は「他のこどもが騒ぐ、ふざける、真面目に取り組まない」。毎日富士山が見えるし海も近いし景色はいいのにね。人間が悪い。


.NETアプリケーションで遅延バインディングを使ってExcelのオートシェイプをコピー

Excelで画面上の文字にマルを付ける依頼があった。
それも、VBAからではなく、C#の.NETアプリケーションから。
しかも、バージョン違いを吸収するために、遅延バインディングを使え、と。
いろいろ試したところ、シートにマルのオートシェイプを作っておいて、それをコピペする方法でないと不可能と分かった。ところが参照したネット上のTipsは間違っていて、時間を食った。結局、素直にコピペ(Shape.Duplicate + Shape.Cut + WorkSheet.Paste)するのが一番良いとわかった。恐ろしく面倒なコードになった。


// Excelアプリケーションオブジェクト
private object xlsApplication = null;
// Workbooksオブジェクト
private object xlsBooks = null;

// Excelアプリケーションオブジェクト
protected object XlsApplication
{
get
{
// 存在しない場合は作成する
if (xlsApplication == null)
{
Type classType = Type.GetTypeFromProgID(“Excel.Application”);
xlsApplication = Activator.CreateInstance(classType);
}
return xlsApplication;
}
}

// Workbooksオブジェクト
protected object Workbooks
{
get
{
if (xlsBooks == null)
{
xlsBooks = XlsApplication.GetType().InvokeMember(“Workbooks”,
BindingFlags.GetProperty, null, XlsApplication, null);
}
return xlsBooks;
}
}

//あらかじめshapeSheetNameシートにitemNameというオートシェイプが作成されているものとして、
//sheetNameシートにnewItemNameという名前でオートシェイプを座標(x,y)にコピーする
public void CopyShape(int bookIndex, string sheetName, string shapeSheetName,
string itemName, string newItemName, float x, float y)
{
object book = null;
object sheets = null;
object sheet = null;
object shapeSheet = null;
object shapesFrom = null;
object shapesTo = null;
object shape = null;
object newShape = null;
object newShape2 = null;

try
{
// WorkBooksは
// WorkbooksオブジェクトからBookオブジェクトを取得
book = Workbooks.GetType().InvokeMember(“Item”, BindingFlags.GetProperty,
null, Workbooks, new object[]{ bookIndex });

// BookオブジェクトからSheetsオブジェクトを取得
sheets = book.GetType().InvokeMember(“Worksheets”, BindingFlags.GetProperty,
null, book, null);

// SheetsオブジェクトからSheetオブジェクトを取得
sheet = sheets.GetType().InvokeMember(“Item”, BindingFlags.GetProperty,
null, sheets, new object[]{ sheetName} );

// Shapeのあるシートをゲット
shapeSheet = sheets.GetType().InvokeMember(“Item”, BindingFlags.GetProperty,
null, sheets, new object[]{ shapeSheetName} );

//Shapeをゲッツ
shapesFrom = shapeSheet.GetType().InvokeMember(“Shapes”, BindingFlags.GetProperty,
null, shapeSheet, null);
shape = shapesFrom.GetType().InvokeMember(“Item”, BindingFlags.InvokeMethod,
null, shapesFrom, new object[] { itemName });

//コピペする
newShape = shape.GetType().InvokeMember(“Duplicate”, BindingFlags.InvokeMethod,
null, shape, null);
newShape.GetType().InvokeMember(“Name”, BindingFlags.SetProperty,
null, newShape, new object[] { newItemName });
newShape.GetType().InvokeMember(“Cut”, BindingFlags.InvokeMethod, null, newShape, null);
sheet.GetType().InvokeMember(“Paste”, BindingFlags.InvokeMethod,
null, sheet, null);

//配置
shapesTo = sheet.GetType().InvokeMember(“Shapes”, BindingFlags.GetProperty,
null, sheet, null);
newShape2 = shapesTo.GetType().InvokeMember(“Item”, BindingFlags.InvokeMethod,
null, shapesTo, new object[] { newItemName });
newShape2.GetType().InvokeMember(“Left”, BindingFlags.SetProperty,
null, newShape2, new object[] { x });
newShape2.GetType().InvokeMember(“Top”, BindingFlags.SetProperty,
null, newShape2, new object[] { y });
}
finally
{
//COMオブジェクトの掃除
ReleaseComObject(newShape2);
ReleaseComObject(newShape);
ReleaseComObject(shape);
ReleaseComObject(shapesTo);
ReleaseComObject(shapesFrom);
ReleaseComObject(shapeSheet);
ReleaseComObject(sheet);
ReleaseComObject(sheets);
ReleaseComObject(book);
}
}


最短経路導出

最短経路導出論理を作成した。考えた案は次の通り。


1)デフォルトで、前回作成した最短経路予想を最短経路とする
2)駅をノード化して、子ノード(複数)と親ノードを持てるようにする
−後楽園−本郷三丁目−御茶ノ水
  |
−飯田橋−九段下−
  |
市ケ谷
なら、
後楽園(親)
本郷三丁目(子1)
御茶ノ水(子1の子)
飯田橋(子2)
九段下(子2の子1)
市ケ谷(子2の子2)
のように。
3)開始駅を決めたら、隣接駅全部のノードを作り、子ノードとして追加
4)再帰処理で順次ノードを作るが、子ノードを追加してはいけないルールに合致したら追加しない
[4-a]親ノードに存在する駅(逆戻り、無限ループの防止)
[4-b]行き止まりの駅に着いたが目的地の駅と異なる場合
[4-c]現時点の最短経路と比べて運賃が高くなる場合、または同額だが積算キロ数が多くなる場合
5)運よく目的地の駅に着いたら、[4-c]の論理を使って最短経路と比べる。
最短経路より安い・短い場合は更新する
以上で、子ノードの追加が終わった時点で計算が終了するだろう、と考えて実装した。テストしていたら、東京メトロ内で最長区間の和光市→西船橋を計算したところでブラウザごとフリーズしてしまった。全然計算が終わらない。
で、3)の処理を変えて

3改)隣接する乗換駅 or 終点のノードを作り、子ノードとして追加
上の例なら乗換駅のない本郷三丁目とお茶の水はカット
と、ノードの数を減らしてみたら、劇的に早くなった。和光市→西船橋でも0.1秒くらいで終わる。
ただテストで10件ほど計算してみたけれどどれも最短経路予想と違わない結果になるのが気になる。何か問題あるんじゃないか。
これ以上のデバッグは後回しにして、次回は運賃表示とマップの保存に対応しよう。
仕事が苦しいので他のプログラム作成に情熱を燃やして発散しようと思っていたら仕事以外でもデバッグばかりする羽目になってしまった。


ラジオ

聞く力が足りないのでラジオを聴こうと思い立った。ラジオを録音するソフトとしてradikaがある。4月からradikoの仕様が変わって、一部のファイルを変更しないと再生も録音もできなくなったが、このソフトは非常に便利で、番組表をゲットして簡単に予約録音ができる。多重録音も可能だ。
早速、1週間分の気になる番組を全部録音した。合計30時間くらいあるのではと思われる。好きな番組ができたら、毎週録ってみよう。
また、GetAsfStreamを使えば、海外のラジオ番組も予約録音できる。これは番組表や時差と照らし合わせる必要があるので少々面倒だが、Clover.FMと組み合わせれば全世界のラジオの視聴と録音ができる。好きな放送局を選んだらページのソースを見て、mms://で始まるアドレスをGetAsfStreamにコピペしてやればよい。これで音声教材には困らなさそうだ。


戦力外通告

引っ越しの準備もたけなわ、段ボールがみるみる積みあがっていく。その数20個程度。毎回引っ越しに100個ほど使うので、これで1/5程度か。
しかし私は、荷造りをしていない。なぜか。
数日前、段ボールが来る前に本棚の掃除をしていた。ホコリを拭いておけば、あとは詰めるだけですむから。ところが、どれだけ注意力がないのか、部屋の隅っこの本棚がカビまるけなのを見逃してしまったり、本を拭いて戻す時に数冊角を曲げてしまったりしていた。
このためお叱りを受け、荷造り担当から外されることになった。


FareMap

六帖Webアプリに、FareMapを追加した。思ったよりも進まなかった。
・DB読み込み、データ構造作成
・座標情報を基に線路描画
・最短経路予想機能
・運賃計算(東京メトロ分のみ)
まで完成した。ただこの最短経路予想、都心部ではやはり乗り換え過ぎてしまい本当の最短経路になりにくい。まだ無限ループを防ぐ機能がなく、まだ試していないが全駅を対象にすると無限ループになる可能性がある。
線路描画はそれなりの形になったけれど、駅が密集している部分では駅名が重なってしまっている。これは編集機能を付ければなんとなかるだろう。
まだ実装しなければならないのは
・最安値経路算出
・路線図編集・保存・読み込み機能
・運賃一覧表示
・さらなる路線の追加、メトロ都営直通割引、JRの複雑な運賃計算
先は長い。日曜だというのに本業より遥かに面白いからのめり込んでしまって、疲れた。


FareMap(仮称)

地下鉄が複雑すぎるので、経路の組み合わせが天文学的に増えるおそれを解消するため、まず経路の予想を立ててから計算しよう、と考えた。
1.駅情報に、運賃計算のための営業キロに加えて、緯度と経度を導入する。
2.まず最短経路予想をする。始点から終点までの緯度と経度を比較して、上りと下りどっちに進めば目的地に付きそうかを判断しながら、1本経路を作る。
例えば丸ノ内線新宿駅から半蔵門線半蔵門駅に行きたければ、新宿と半蔵門を比較すると半蔵門は東北方向にあるから荻窪行きではなく池袋行き方面に進めばいいだろうなぁ、と予想する。
で、最短経路の運賃を先に計算しておく。
3.乗り換え駅ごとに分岐して経路探索する。ただし、探索中に最短経路の運賃をオーバーしたらそこの経路は捨てる。
4.乗り換えのない終点駅について、しかも目的地に到達しない経路は捨てる。
5.最短経路より運賃が安い経路があったら、最安値を更新してそれより運賃の高い経路を全部捨てる。
以上で、計算量をかなり減らせるのではないか。まず東京メトロ版から作ってみようと思う。


出勤

1か月に1回の東京への出勤日。今日は土曜なので、平日と小田急の客層が違う。平日は都心へ向かうリーマンばかりだが、今日は学生風ばかり。社内でスマホを見てる人の率が20%→40%と大幅に増える。本を読んでいる人の率は30%→10%に大幅ダウン。でも一番多いのは、寝てる人。
1時間半程度電車で過ごした後、職場まで徒歩25分。長距離通勤するのは、今日が最後となる。


仕事終わった。長時間嫌気がさしてくる中、次のようなことを妄想していた。


運賃表が欲しい。
駅に行くと、路線図と運賃一覧が書いてある表、あれ。
理想としては、路線図上の駅をクリックすると他のすべての駅に一度に運賃が表示されるのがよい。
昔JRでしかも首都圏に限ればあったんだけど、消費増税とともに公開停止してしまった。
主に必要な機能は、
・路線図の作成、保存
・運賃の計算
の2点。
どちらも複雑で大変だ。路線図は、ドラッグ&ドロップして線路を配置するような、凝ったGUIにしてみたい。
運賃計算は、田舎なら楽勝だけど都心の地下鉄網となると、乗り換えのパターンが膨大なのとJRの特例計算てんこ盛りなので、どこまでできるだろうか。検索したら大学の論文になってるくらい難しいようだ。論文だけ見ると簡単なif文があるだけのように見えるけれど。
JRだけでなく首都圏の鉄道全部を盛り込んでみたいものだ。