2022/05,江端さんの忘備録

連休中ですが、普段より働いている気がします ―― 疲労を感じます。

I'm on holiday, but I feel like I'm working more than usual -- I feel tired.

「『手を抜くため』の努力は惜しまない」という方針で、以下のようなことで、ずっと悩んでいます。

Based on my policy of "making every effort to 'cut corners'" , I am struggling with following issues.

お願い:PostGISで、曲線のジオメトリをそのまま使って、始点と距離を与えて、終点の座標を得たいです

そういえば、今回、自分で作った本が、自分を助けてくれて、本当に助かっています。

Come to think of it, the book I made myself before really helped me out.

『ありがとう!過去の私!!』と、時空を越えて、自分に感謝しております。

'Thank you, past me!' And I thank myself for crossing time and space.

------

私、最近、teratail(https://teratail.com/)という技術系の質問掲示板を使わせて頂いておりまして ―― 質問ばかりしています。

I have recently been using a technical question board called teratail (https://teratail.com/) -- I ask a lot of questions.

あまりにも申し訳ないので、私でも、答えられることには回答したいと思っています。

I am too sorry to do this, but even I would like to answer what I can.

それが『トレードオフ』・・・ではなくて、えーっと、『共助共存』というやつですよね、うん、そう。

That's the 'trade-off'...No, I don't mean that. Let's see, it's called 'mutual aid coexistence', yes, yes.

しかし、私が答えられそうなことは、「回答済み」か「解決済み」になっており、私に口を出せる余地がありません。

However, anything I might be able to answer is either "answered" or "resolved", leaving no room for me to speak.

-----

『いつか、あなたを助けるから、今はちょっと勘弁ね』と呟きながら ――

With muttering, 'Someday, I'll help you, just not right now,'

今日も、パソコンの前で、宇都宮市のGISデータベース構築と利用方法に、頭を抱えています。

Today, I am sitting in front of my computer with giving my headache, and trying the construction and use of the GIS database for Utsunomiya City.

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

「OpenStreetMapのデータから鉄道だけを抽出してGeoJSONで出力する方法」を試してみた件

を前提として、osmから道路と鉄道路線のみを残して残りは消去する方法を探してみました。

どうやら、これでできるようです。

まず、宇都宮地区を抽出します。

osmium extract --bbox 139.76675736787732,36.47004171587971,140.1596765021647,36.659949299138 -o utunomiya.osm.pbf kanto-latest.osm.pbf

次に、以下を行います。

root@2e317961f1d0:/tmp# osmium tags-filter utunomiya.osm.pbf nw/railway nw/highway=motorway,service,primary,secondary,tertiary,unclassified,residential -o utunomiya-lrt-latest.osm.pbf

上記のコマンドのnw(r)は、以下のような意味になっています。

nwr は、OpenStreetMapの nodewayrelationsを指定しています。

nw/(キー)=(タグ),(タグ),(タグ),(タグ),(タグ).....

という記述をします。

(キー)は、https://taginfo.openstreetmap.org/keys を参照

(タグ)は、https://taginfo.openstreetmap.org/tags を参照するといいでしょう。

https://wiki.openstreetmap.org/wiki/JA:Tag:highway%3Dresidential の、

上記をクリックすると、motorway,service,primary,secondary,tertiary,unclassified,residential 

の意味が分かります(多分)。

2022/05,江端さんの忘備録

BS世界のドキュメンタリー 「女王陛下の戴冠式」を見ていました。

I was watching the BS World Documentary "The Coronation of Her Majesty the Queen".

エリザベス2世と、当時の関係者のコメント付きの、戴冠式のイベントに至るまでの16ヶ月間の準備の記録ドキュメンタリーです。

This documentary chronicles the 16 months of preparation leading up to the coronation event, with comments from Queen Elizabeth II and others involved at the time.

私、こういう、イベントの裏側の話が大好きで、楽しく見ていました。

I love these behind-the-scenes stories of events and enjoyed watching them.

-----

長女が、「これって、女王にいくらギャラ払われるのかな」と言っていました。

My senior daughter said, "I wonder how much the queen will be paid for this".

「多分、女王陛下は、ギャラ受けとっていないと思うぞ」と、私は答えました。

I replied, "I don't think Her Majesty the Queen is getting paid."

長女:「なんで、英国では、こういう番組を作れるんだろう」

Senior Daughter: "Why, in the UK, can they make these programs?"

江端:「多分、まあ、良い意味でも、悪い意味でも、英国皇室は、国民に対して『開かれている』のは確かだろう」

Ebata: "Perhaps, well, in a good way or a bad way, the British imperial family is certainly 'open' to the public."

離婚後とは言え、『元皇太子妃殿下を、パパラッチが追い回して交通事故死を至らしめる』なんてことは、日本の皇室では、ちょっと想像できません。

Even after the divorce, it is hard to imagine the paparazzi chasing the former Crown Princess to death in a car accident in the Japanese Imperial Family.

-----

江端:「個人的には、10年後でもいいけど、私は、天皇陛下即位の儀の記録ドキュメンタリー見たいな」

Ebata: "Personally, I'd like to watch a documentary recording the Emperor's accession to the throne, even if it's 10 years later."

というと、嫁さんは、

On the other hand, the wife said.

『私は、見たくないな』

"I don't want to watch that."

と言っていました。

まあ、皇室の『開閉度』は、皇室や宮内庁の判断だけでなく、私たち国民の意識も、大きなパラメタなんだろうな、と、思いました。

Well, I thought that the "degree of opening and closing" of the Imperial Family is not only the judgment of the Imperial Family and the Imperial Household Agency, but also our public awareness might be a major parameter.

未分類

どなたか、ご教示頂きたく、お願い致します。

前提

Windows10でDockerを使って、PostgreSQL + PostGISの環境を使っています。

実現したいこと

PostGISで、曲線のジオメトリをそのまま使って、始点と距離を与えて、終点の座標を得たいです。
具体的には、イメージ説明に示すように、

(1)図中のジオメトリで与えられる地図上の曲線において、
(2)始点(A点)の座標(緯度経度)と距離を入力して、
(3)終点(B点)の座標(緯度経度)を取得する

を実現したいです。

検討した事項

geometry型がさっぱり分からん

にも記載しておりますが、曲線のジオメトリは、 ST_Astext()を使うことで、座標に展開できるので、これを使って地道に計算(足し算して端数は比率を乗算する)すれば、算出は可能だと思うのですが、非常に迂遠な計算で、計算負荷も大きくなると思います。

そこで、ジオメトリ(the geom)を直接使って、そのようなことを実現できる関数を
https://postgis.net/docs/manual-3.2/postgis-ja.html#reference
から、探してみたのですが、見付けられませんでした(見落している可能性大です)。

補足情報

大量のエージェントを地図上で動かすシミュレーションで、使うことを想定しており、可能な限りPostGISで提供されている関数とジオメトリをそのまま使って実現したいと考えております。

連絡先

(この質問に関するご報告のみ、ご利用頂きたくお願い致します)

何卒、よろしくお願い致します。

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

http://kobore.net/test1031-2.cpp

/////////////////////////////////////////
// 
//
// 適当な2つの座標を指定して、そのルートをトレースする
// 
//  ST_Distance, ST_DWithinの使い方
//



/*

gcc -g -I"D:\PostgreSQL\pg96\include" test1031-2.cpp -o test1031-2.exe -L"D:\PostgreSQL\pg96\lib" -llibpq -lwsock32 -lws2_32

*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <math.h>
#include "libpq-fe.h"

struct PERSON{  
  double p_x; // 現在のX座標
  double p_y; // 現在のY座標
};

#define rad2deg(a) ((a)/M_PI * 180.0) /* rad を deg に換算するマクロ関数 */
#define deg2rad(a) ((a)/180.0 * M_PI) /* deg を rad に換算するマクロ関数 */

double distance_km(double a_longitude, 
				   double a_latitude, 
				   double b_longitude, 
				   double b_latitude, 
				   double *rad_up)				   				 
{
  double earth_r = 6378.137;

  double loRe = deg2rad(b_longitude - a_longitude); // 東西  経度は135度
  double laRe = deg2rad(b_latitude - a_latitude); // 南北  緯度は34度39分

  double EWD = cos(deg2rad(a_latitude))*earth_r*loRe; // 東西距離
  double NSD = earth_r*laRe; //南北距離

  double distance = sqrt(pow(NSD,2)+pow(EWD,2));  
  *rad_up = atan2(NSD, EWD);

  return distance;
}


double diff_longitude(double diff_p_x, double latitude) 
{
  double earth_r = 6378.137;
  // ↓ これが正解だけど、
  double loRe = diff_p_x / earth_r / cos(deg2rad(latitude)); // 東西
  // 面倒なので、これで統一しよう(あまり差が出ないしね)
  //double loRe = diff_p_x / earth_r / cos(deg2rad(35.700759)); // 東西
  double diff_lo = rad2deg(loRe); // 東西

  return diff_lo; // 東西
}
  
double diff_latitude(double diff_p_y) 
{
  double earth_r = 6378.137;
  double laRe = diff_p_y / earth_r;  // 南北
  double diff_la = rad2deg(laRe); // 南北
  
  return diff_la; // 南北
}

int main (){

  PERSON test_person;

  // 適当な2つの座標
  double dep_x = 139.46507, dep_y = 35.59577;
  double arr_x = 139.47627, arr_y = 35.60358;

  const char *conninfo = "host=localhost user=postgres password=c-anemone dbname=city_routing";
  
  // データベースとの接続を確立する 
  PGconn *conn = PQconnectdb(conninfo);
  PGresult *res;

  // バックエンドとの接続確立に成功したかを確認する
  if (PQstatus(conn) != CONNECTION_OK){
	fprintf(stderr, "Connection to database failed: %s",
			PQerrorMessage(conn));
  }
  
  /////////////////////// startのもっとも近いノード番号を検索する ////////////////////////
  
  char stringSQL[1024] = {0};  
  
  // 半径300メートルくらいで検索
  sprintf(stringSQL,"SELECT source, ST_Distance('SRID=4326;POINT(%f %f)'::GEOGRAPHY, the_geom) as dist FROM ways WHERE ST_DWithin(the_geom, ST_GeographyFromText('SRID=4326;POINT(%f %f)'), 300.0) ORDER BY dist;", dep_x, dep_y, dep_x, dep_y);
  
  res = PQexec(conn, stringSQL);
	
  if (res == NULL){
	fprintf(stderr, "failed: %s",
			PQerrorMessage(conn));
  }
  
  // SELECTの場合戻り値は PGRES_TUPLES_OK.  これを確認しておく
  if (PQresultStatus(res) != PGRES_TUPLES_OK){
	PQerrorMessage(conn);
  }
  
  int nFields = PQnfields(res);
  
  // そして行を表示する。
  int i_count = 0;
  
  while( atof(PQgetvalue(res, i_count, 1)) == 0.0){
	i_count += 1;
  }
  
  double dist = atof(PQgetvalue(res, i_count, 1));
  int start_source = atoi(PQgetvalue(res, i_count, 0));
  
  PQclear(res); // SQL文の発行に対して必ずクリアする
  
  /////////////////////// startとstartのもっとも近いノードの距離を計測する ////////////////////////  
  
  memset( stringSQL, 0, sizeof(stringSQL)); // 念の為クリア

  sprintf(stringSQL,"SELECT ST_Distance('SRID=4326;POINT(%f %f)'::GEOGRAPHY, the_geom) from ways where source = %d;", dep_x, dep_y, start_source );

  res = PQexec(conn, stringSQL);  

  if (res == NULL){
	fprintf(stderr, "failed: %s",
			PQerrorMessage(conn));
  }
  
  // SELECTの場合戻り値は PGRES_TUPLES_OK.  これを確認しておく
  if (PQresultStatus(res) != PGRES_TUPLES_OK){
	PQerrorMessage(conn);
  }
  
  nFields = PQnfields(res);

  double dep_dist = atof(PQgetvalue(res, 0, 0));  

  PQclear(res); // SQL文の発行に対して必ずクリアする


  ////////////////////// endのもっとも近いノード番号を検索する///////////////////////////

  memset( stringSQL, 0, sizeof(stringSQL)); // 念の為クリア
  
  // 半径300メートルくらいで検索
  sprintf(stringSQL,"SELECT source, ST_Distance('SRID=4326;POINT(%f %f)'::GEOGRAPHY, the_geom) as dist FROM ways WHERE ST_DWithin(the_geom, ST_GeographyFromText('SRID=4326;POINT(%f %f)'), 300.0) ORDER BY dist;", arr_x, arr_y, arr_x, arr_y);

  res = PQexec(conn, stringSQL);
	
  if (res == NULL){
	fprintf(stderr, "failed: %s",
			PQerrorMessage(conn));
  }
  
  // SELECTの場合戻り値は PGRES_TUPLES_OK.  これを確認しておく
  if (PQresultStatus(res) != PGRES_TUPLES_OK){
	PQerrorMessage(conn);
  }
  
  nFields = PQnfields(res);
  
  // そして行を表示する。
  i_count = 0;
  
  while( atof(PQgetvalue(res, i_count, 1)) == 0.0){
	i_count += 1;
  }
  
  dist = atof(PQgetvalue(res, i_count, 1));
  int end_source = atoi(PQgetvalue(res, i_count, 0));
  
  PQclear(res); // SQL文の発行に対して必ずクリアする

  /////////////////////// endとendのもっとも近いノードの距離を計測する ////////////////////////  
  
  memset( stringSQL, 0, sizeof(stringSQL)); // 念の為クリア

  sprintf(stringSQL,"SELECT ST_Distance('SRID=4326;POINT(%f %f)'::GEOGRAPHY, the_geom) from ways where source = %d;", arr_x, arr_y, end_source );

  res = PQexec(conn, stringSQL);  

  if (res == NULL){
	fprintf(stderr, "failed: %s",
			PQerrorMessage(conn));
  }
  
  // SELECTの場合戻り値は PGRES_TUPLES_OK.  これを確認しておく
  if (PQresultStatus(res) != PGRES_TUPLES_OK){
	PQerrorMessage(conn);
  }
  
  nFields = PQnfields(res);

  double arr_dist = atof(PQgetvalue(res, 0, 0));  

  PQclear(res); // SQL文の発行に対して必ずクリアする

  printf("start_source:%d end_source:%d\n",start_source, end_source);
  printf("dep_dist:%f arr_dist:%f\n",dep_dist, arr_dist);


  
  ////////////////// start_sourceとend_sourceのノードをダイクストラで計算する //////////////////////

  printf("%-15f,%-15f\n",dep_x, dep_y); // 出発点を書き込んでおく

  memset( stringSQL, 0, sizeof(stringSQL)); // 念の為クリア

  sprintf(stringSQL, "SELECT node, edge FROM pgr_dijkstra('SELECT gid as id, source, target, cost_s As cost, reverse_cost_s AS reverse_cost FROM ways', %d, %d, true );",start_source, end_source);

  res = PQexec(conn, stringSQL);  

  if (res == NULL){
	fprintf(stderr, "failed: %s",
			PQerrorMessage(conn));
  }
  
  // SELECTの場合戻り値は PGRES_TUPLES_OK.  これを確認しておく
  if (PQresultStatus(res) != PGRES_TUPLES_OK){
	PQerrorMessage(conn);
  }
  
  nFields = PQnfields(res);

  // start_sourceとend_sourceのノードのノード番号の一つをゲットする処理
  for (int i = 0; i < PQntuples(res)-1  ; i++) {  // バス停とバス停の間のノードを全部出す
	  
	/* 
	   ここでは、両端のエッジの座標が必要になる。
	   pgr_dijkstraでは、エッジ(両端)情報(x1,y1,x2,y2)が取得できるのだけど、
	   どっちが先端でどっちが終端かが分からない。
	   
	   そこで(恐しく迂遠だけえど)、まずエッジ情報(x1,y1,x2,y2)を得てから、
	   ノード情報(x1, y1)を取得する(ちなみにノード情報のx2,y2は、
	   交差点などの場合は複数出てくるので、信用してはならない)。
	   
	   で、ノード情報のx1,y1を先端として、エッジ情報のx1,y1とx2,y2と一致していない方を終端とする
	   という処理を取っている。
	   
	   (もっと簡単な方法があったら、誰か教えて下さい)
	   
	*/
	
	double node[2] = {0.0}; // x1, y1
	double edge[4] = {0.0}; // x1, y1, x2, y2
	
	int dummy_int_1 = 0;
	int dummy_int_2 = 0;
	
	for (int j = 0; j < nFields; j++) { // (j=0:node(source)と j=1:edge(gid)の値をゲットする)
	  
	  // まずノードの方
	  if (j == 0){//(j=0:node(source)
		
		memset( stringSQL, 0, sizeof(stringSQL)); // 念の為クリア
		sprintf(stringSQL, "SELECT x1,y1 from ways where source = %s;",
				PQgetvalue(res, i, j)); // ノードの座標を得る
		
		dummy_int_1 = atof(PQgetvalue(res, i, j));
		
		PGresult *res2 = PQexec(conn, stringSQL);
		if (res2 == NULL){
		  fprintf(stderr, "failed: %s",
				  PQerrorMessage(conn));
		}
		
		
		int nFields2 = PQnfields(res2);
		
		for (int j2 = 0; j2 < nFields2; j2++) { // node(source)のx1,y1の2つの値
		  // printf("%-15s", PQgetvalue(res2, 0, j2));
		  node[j2] = atof(PQgetvalue(res2, 0, j2));  
		}
		
		PQclear(res2); // SQL文の発行に対して必ずクリアする
	  }
	  
	  //次にエッジの方
	  if (j == 1){//(j=1:edge(gid)
		
		memset( stringSQL, 0, sizeof(stringSQL)); // 念の為クリア
		
		sprintf(stringSQL, "SELECT x1,y1,x2,y2 from ways where gid = %s;",
				PQgetvalue(res, i, j)); // ノードの座標を得る
		
		dummy_int_2 = atof(PQgetvalue(res, i, j));
		
		PGresult *res2 = PQexec(conn, stringSQL);
		if (res2 == NULL){
		  fprintf(stderr, "failed: %s",
				  PQerrorMessage(conn));
		}
		
		int nFields2 = PQnfields(res2);
		
		for (int j2 = 0; j2 < nFields2; j2++) { // node(source)のx1,y1の2つの値
		  // printf("%-15s", PQgetvalue(res2, 0, j2));
		  edge[j2] = atof(PQgetvalue(res2, 0, j2));  
		}
		
		PQclear(res2); // SQL文の発行に対して必ずクリアする
		
	  }
	}
	
	double start_x, start_y, end_x, end_y;
	
	//出揃ったところで、始点と終点の判定を行う
	if ((fabs(node[0] - edge[0]) < 1e-6) && (fabs(node[1] - edge[1]) < 1e-6)){
	  start_x = edge[0];
	  start_y = edge[1];
	  end_x = edge[2];
	  end_y = edge[3];
	}else{
	  start_x = edge[2];
	  start_y = edge[3];
	  end_x = edge[0];
	  end_y = edge[1];
	}
	
	// printf("両端の確定 %f,%f,%f,%f\n", start_x, start_y, end_x, end_y);
	
	//両端の進行方向(ベクトル)を計算する
	double rad_up1;
	distance_km(start_x, start_y, end_x, end_y, &rad_up1);
	
	// 確定直後にバス位置は、出発点(start_x, start_y)に強制移動する
	test_person.p_x = start_x;
	test_person.p_y = start_y;
	
	// ここから0.1m単位でグルグル回す

	int do_flag = 0;
	do{
	  // printf("x=%-15f,y=%-15f\n",test_person.p_x, test_person.p_y);
	  printf("%-15f,%-15f\n",test_person.p_x, test_person.p_y);
	  
	  // 以下の0.1は1サンプリング0.1メートルを擬制
	  test_person.p_x += diff_longitude(0.1 * cos(rad_up1), test_person.p_y);	
	  test_person.p_y += diff_latitude(0.1 * sin(rad_up1));
	  
	  double rad0 = atan2((end_y - start_y),(end_x - start_x));
	  double rad1 = atan2((end_y - test_person.p_y),(end_x - test_person.p_x));
	  
	  // ここは、http://kobore.net/over90.jpg で解説してある
	  
	  if (fabs(rad0 - rad1) >= 3.141592654 * 0.5){
		// 終点越えの場合、終点に座標を矯正する
		test_person.p_x = end_x;
		test_person.p_y = end_y;
		
		do_flag = 1; // フラグを上げろ
	  }
	  
	}while(do_flag == 0);
	
  } // バス停とバス停の間のノードを全部出す
  
  PQclear(res); // SQL文の発行に対して必ずクリアする
  
  printf("%-15f,%-15f\n",arr_x, arr_y); // 終着点を書き込んでおく

}

上記のプログラムと同じような振舞をするプログラムをGolangで書いてみました。

// go get github.com/lib/pq を忘れずに
// go run main7.go

/*
	前提は、https://ebata-books.booth.pm/items/3351408 の環境

	(1)スタート地点  139.91804656152837 36.57246810341353,
	(2)ゴール地点 139.93515104919825 36.55883778950532
	(3)とした時、この2点に最も近いノードを探し出して、
	(4)その2点をダイクストラ法で、最短距離探索をするプログラム

	分かったこと
	上記(3)のノードが、孤立ノードになっている場合があり、この場合(4)が実施できないので、
	ダイクストラに失敗する場合は、別のノードでトライアルする必要あり
	(このコードでは展開しないけど)
*/

package main

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

	_ "github.com/lib/pq"
)

var source int
var longitude float64
var latitude float64
var dist float64

func rad2deg(a float64) float64 {
	return a / math.Pi * 180.0
}

func deg2rad(a float64) float64 {
	return a / 180.0 * math.Pi
}

func distance_km(a_longitude, a_latitude, b_longitude, b_latitude float64) (float64, float64) {
	earth_r := 6378.137

	loRe := deg2rad(b_longitude - a_longitude) // 東西  経度は135度
	laRe := deg2rad(b_latitude - a_latitude)   // 南北  緯度は34度39分

	EWD := math.Cos(deg2rad(a_latitude)) * earth_r * loRe // 東西距離
	NSD := earth_r * laRe                                 //南北距離

	distance_km := math.Sqrt(math.Pow(NSD, 2) + math.Pow(EWD, 2))
	rad_up := math.Atan2(NSD, EWD)

	return distance_km, rad_up
}

func diff_longitude(diff_p_x, latitude float64) float64 {

	earth_r := 6378.137
	// ↓ これが正解だけど、
	loRe := diff_p_x / earth_r / math.Cos(deg2rad(latitude)) // 東西
	// 面倒なので、これで統一しよう(あまり差が出ないしね)
	//loRe := diff_p_x / earth_r / math.Cos(deg2rad(35.700759)) // 東西
	diff_lo := rad2deg(loRe) // 東西

	return diff_lo // 東西
}

func diff_latitude(diff_p_y float64) float64 {
	earth_r := 6378.137
	laRe := diff_p_y / earth_r // 南北
	diff_la := rad2deg(laRe)   // 南北

	return diff_la // 南北
}

func sub_main() {

	x1, y1 := 139.91804656152837, 36.57246810341353
	x2, y2 := 139.93515104919825, 36.55883778950532

	d_km, rad_up := distance_km(x1, y1, x2, y2)

	//x1 += diff_longitude(0.00566*math.Cos(rad_up), y1)
	//y1 += diff_latitude(0.00566 * math.Sin(rad_up))

	x1 += diff_longitude(1.0*math.Cos(rad_up), y1)
	y1 += diff_latitude(1.0 * math.Sin(rad_up))

	fmt.Println(d_km, rad_up)
	fmt.Println(x1, y1)

	d_km, rad_up = distance_km(x1, y1, x2, y2)

	//x1 += diff_longitude(0.00566*math.Cos(rad_up), y1)
	//y1 += diff_latitude(0.00566 * math.Sin(rad_up))

	x1 += diff_longitude(1.0*math.Cos(rad_up), y1)
	y1 += diff_latitude(1.0 * math.Sin(rad_up))

	fmt.Println(d_km, rad_up)
	fmt.Println(x1, y1)

}

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

	// スタート地点  139.91804656152837 36.57246810341353
	rows, err := db.Query("SELECT source, x1 as longitude, y1 as latitude, ST_Distance('SRID=4326;POINT(139.91804656152837 36.57246810341353)'::GEOGRAPHY, the_geom) as dist FROM ways WHERE ST_DWithin(the_geom, ST_GeographyFromText('SRID=4326;POINT(139.91804656152837 36.57246810341353)'), 300.0) ORDER BY dist LIMIT 1")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	for rows.Next() {

		if err := rows.Scan(&source, &longitude, &latitude, &dist); err != nil {
			fmt.Println(err)
		}
		fmt.Println(source, longitude, latitude, dist)
	}

	o_source := source

	// ゴール地点 139.93515104919825 36.55883778950532
	rows, err = db.Query("SELECT source, x1 as longitude, y1 as latitude, ST_Distance('SRID=4326;POINT(139.93515104919825 36.55883778950532)'::GEOGRAPHY, the_geom) as dist FROM ways WHERE ST_DWithin(the_geom, ST_GeographyFromText('SRID=4326;POINT(139.93515104919825 36.55883778950532)'), 300.0) ORDER BY dist LIMIT 1")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	for rows.Next() {

		if err := rows.Scan(&source, &longitude, &latitude, &dist); err != nil {
			fmt.Println(err)
		}
		fmt.Println(source, longitude, latitude, dist)
	}

	d_source := source

	fmt.Println(o_source, d_source)

	//rows, err = db.Query("select x1,y1 from ways where source in (SELECT node as source FROM pgr_dijkstra('SELECT gid as id, source, target,length_m as cost FROM ways', $1::bigint , $2::bigint , false))", o_source, d_source)
	rows, err = db.Query("SELECT node as source FROM pgr_dijkstra('SELECT gid as id, source, target,length_m as cost FROM ways', $1::bigint , $2::bigint , false)", o_source, d_source)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	fmt.Println("2")

	x1, y1 := -1.0, -1.0
	_x1, _y1, _x2, _y2 := -1.0, -1.0, -1.0, -1.0
	px, py := -1.0, -1.0
	flag := 0
	f_flag := 0

	for rows.Next() {

		if err := rows.Scan(&source); err != nil {
			fmt.Println(err)
		}

		rows2, err := db.Query("SELECT source, x1, y1 from ways where source = $1::bigint limit 1", source)
		if err != nil {
			log.Fatal(err)
		}

		for rows2.Next() {

			if f_flag == 0 { //初回だけ2居合入力
				if err := rows2.Scan(&source, &x1, &y1); err != nil {
					fmt.Println(err)
				}
				_x1, _y1 = x1, y1
				//fmt.Println(_x1, _y1)
				f_flag = 1
				continue
			}

			if err := rows2.Scan(&source, &x1, &y1); err != nil {
				fmt.Println(err)
			}
			_x2, _y2 = x1, y1

			//fmt.Println(_x2, ",", _y2)
			_, rad_up := distance_km(_x1, _y1, _x2, _y2)
			//fmt.Println("_x1,_y1,_x2,_y2, rad_up:", _x1, _y1, _x2, _y2, rad_up)

			px, py = _x1, _y1

			for {

				//平均時速20km サンプリング時間は、3.6秒  平均速度は 5.56m 20m/3.6秒
				// 悩むところだな、サンプリング時間を1秒にするか3.6秒にするか → 1秒にするか。
				// 距離端数は切り捨てにして、実質は、平均時速を20km以下としよう
				// (カーブでは減速する、と)

				px += diff_longitude(0.00556*math.Cos(rad_up), py)
				py += diff_latitude(0.00556 * math.Sin(rad_up))

				//double rad0 = atan2((end_y - start_y),(end_x - start_x));
				//double rad1 = atan2((end_y - test_person.p_y),(end_x - test_person.p_x));

				rad0 := math.Atan2((_y2 - _y1), (_x2 - _x1))
				rad1 := math.Atan2((_y2 - py), (_x2 - px))
				// ここは、http://kobore.net/over90.jpg で解説してある

				if math.Abs(rad0-rad1) >= math.Pi*0.5 {
					// 終点越えの場合、終点に座標を矯正する
					px, py = _x2, _y2
					flag = 1 // フラグを上げろ
				}

				fmt.Println(px, ",", py)

				if flag == 1 {
					flag = 0
					_x1, _y1 = _x2, _y2
					break
				}
			}
		}
	}

	//平均時速20km サンプリング時間は、3.6秒  平均速度は 5.56m 20m/3.6秒
	// 悩むところだな、サンプリング時間を1秒にするか3.6秒にするか → 1秒にするか。
	// 距離端数は切り捨てにして、実質は、平均時速を20km以下としよう

	if err := db.Ping(); err != nil {
		log.Fatal("PingError: ", err)
	}
}

2022/05,江端さんの忘備録

ロシアは、現在、国際連合から非難決議を受けております。

Russia is currently under a condemnation resolution from the United Nations.

かつて日本は、国際連盟から自ら脱退しました(1933)。

In the past, Japan withdrew itself from the League of Nations (1933).

ロシアは、経済制裁を受けておりますが、世界のエネルギー資源の産出国であり、中国は(事実上の)同盟国です。

Russia is under economic sanctions, but it is the world's energy resource producer and China is a (de facto) ally.

かつての日本も、世界中から経済制裁を受けました(1941年から100%の経済制裁)。

In the past, Japan was also subjected to economic sanctions from all over the world (100% economic sanctions since 1941).

おまけに、我が国のエネルギーの産出量は10%程度で、同盟国はヨーロッパの二ヵ国(ドイツとイタリア)だけで、どちらも、エネルギー供給国ではなく、エネルギーを融通できる輸送ルートもありませんでした(*)。

In addition, our country's energy production was only about 10%, and our only allies were two European countries (Germany and Italy), neither of which was an energy supplier or had transportation routes that could accommodate energy (*).

(*)今回調べてみて驚いたのですが、当時の日本は、結構、原油の生産があったようです。

(*)To my surprise when I looked into this issue, it seems that Japan was producing quite a bit of crude oil at that time.

―― それでも、当時の我が国の国民は、4年間の戦争を耐え凌いだ

-- Yet, the people of our country at that time endured four years of war

もし東京大空襲がなれば、もし原爆投下がなければ、もしソ連の平和条約の一方的破棄がなければ ―― たぶん、もっと長い期間耐えていただろう、と思います。

If it had not been for the air raid on Tokyo, if it had not been for the atomic bombings, if it had not been for the Soviet Union's unilateral abrogation of the peace treaty -- maybe they would have endured for a longer period of time, I think.

ちなみに、国民の戦争に関する厭世気分が発生したのは、空襲が頻発化し始めた戦争の最後の半年程度で、それまで、政府の支持率は、比較的良好でした。

Incidentally, public disillusionment with the war occurred only in the last six months of the war, when air raids began to become more frequent, and until then, government approval ratings had been relatively favorable.

戦争中の政府の支持率は「高い」 ―― これ、結構、常識です(例えば、自殺率が恐しく改善します)。

Support for government during war is "high" -- this is pretty much common knowledge (e.g., suicide rates improve horribly).

日本は戦争に負けましが、ベトナムなんて20年間戦い続けて、最後には米国に勝ちましたし、キューバなんて1960年から経済制裁を受けていて、今なお耐えています。

Japan lost the war, but Vietnam was fought for 20 years and finally won against the U.S. Cuba has been under economic sanctions since 1960 and is still enduring them.

-----

「ロシアによるウクライナ侵攻」について、マスメディアの見解を一方的に受信するだけではなく、自力で調べて、自分で考えた方が良いと思います。

I think you should do your own research and think for yourself about the "Russian invasion of Ukraine" instead of just receiving one-sided views from the mass media.

今回、私は「過去の我が国の太平洋戦争との比較」という観点で考えてみましたが、他にも色々なアプローチがあると思います。

I have looked into this issue from the perspective of "comparisons with our country's past wars in the Pacific," but I am sure there are many other approaches.

未分類

「OpenStreetMapのデータから鉄道だけを抽出してGeoJSONで出力する方法」を試してみた件

で、目的のファイルをjson形式で取れたのですが、私はPostGIS+PostgreSQLに落したいので、osmファイルに変換する必要があると思っていましたが ――

[OSM] .osm.pbfファイルを読み込んでみる1

osm2pgsql: .osm.pbfをPostgreSQLのデータベースに変換するツール

なんだ、pbf->osm変換の手段を探していたんだけど、直接PostGIS + PostgreSQLに変換することができるなら問題ないかな、と。

ただ、osmconvert.exeで変換できるらしいので、とりあえず、これも試したいと思いました。

osmconvertのダウンロード

のサイトが警告がでているのですが、多分、http:// と https://の違いだろうと腹を括ってダウンロードしました。(私は、C:\Users\ebata\docker-osmium-toolに一時的に格納)

C:\Users\ebata\docker-osmium-tool>osmconvert64 utunomiya-railway-latest.osm.pbf > utunomiya-railway-latest.osm

としたところ、

<?xml version='1.0' encoding='UTF-8'?>
<osm version="0.6" generator="osmconvert 0.8.10">
<node id="264180821" lat="36.5606204" lon="139.8988763" version="11" timestamp="2018-11-27T16:20:29Z" changeset="0"/>
<node id="264180832" lat="36.552414" lon="139.8959184" version="3" timestamp="2018-11-27T16:20:29Z" changeset="0"/>
<node id="264180833" lat="36.55513" lon="139.8975756" version="3" timestamp="2016-04-04T20:29:57Z" changeset="0"/>

という感じで、いつもの見慣れた形式の表示がされたので、多分、これで変換できたのだと思います。

サイズは、33KB -> 716KB に跳ね上がったようですが。

 

 

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

「OpenStreetMapから鉄道路線データを落とせない」と愚痴っていたら、社内の人から「OSMに鉄道データ入っていますよ」と言われて、愕然とした件

という訳で、今日は、OpenStreetMapX.jl をダウンロードして色々やっていたのですが、よく分からりませんでした。というか、何ができるのか、が今一つ肚に落ちてこないのです。

で、こちらの路線は諦めて、とにかく鉄道データが取れる手段を探しました。

OpenStreetMapのデータから鉄道だけを抽出してGeoJSONで出力する方法

を試すことにしました。
から、osm.pbfファイルを落してきました。

osmium を使う方法がでていたのですが、これが私のWindows10環境では実現できませんでした。(正確に言うと、私の環境には"cl.exe"というコマンドがないので、コンパイルができない。Visual Studio C++(?)を入れるとできるらしいのですが、これ以上、パソコンの環境を汚したくなったのです)。
で、色々試みたのですが(この間5~6時間)、上手くいかず、dockerでないかなにないかなーと探していたら、見付けました。
1つ目のWindowsのコマンドプロンプト(MSYS64では上手く動かない)かったので、
C:\Users\ebata>docker run -t -i stefda/osmium-tool /bin/bash
で、いきなりDockerのコンテナの中に入りました。

/bin , /usr/bin, /usr/local/bin のどこかで当るだろうと探りを入れていたら、

root@e87f198ccc07:/usr/bin# ls o*
objcopy objdump od odbcinst ogdi-config openssl osage osmium
root@e87f198ccc07:/usr/bin# osmium
Usage: osmium COMMAND [ARG...]

/usr/binで当たりました。

で、作業用のディレクトリを探していたら、/tmpがあったので、ここにファイルを持ち込んで作業することにしました

2つ目の、別のWindowsのコマンドプロンプトを上げて、今動かしているコンテナを調べました

C:\Users\ebata>docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e87f198ccc07 stefda/osmium-tool "/bin/bash" 12 minutes ago Up 12 minutes wonderful_lumiere

で、ローカルからDockerコンテナの/tmpに直接コピーしました。

C:\Users\ebata>docker cp kanto-latest.osm.pbf wonderful_lumiere:/tmp

一つ目のコマンドプロンプトに戻って、ファイルが可能されているか確認しました。

root@e87f198ccc07:/# cd tmp
root@e87f198ccc07:/tmp# ls
kanto-latest.osm.pbf

ちゃんと入っていました。パスも通っているようですので、

root@e87f198ccc07:/tmp# which osmium
/usr/bin/osmium

そのまま、/tmpの中で作業しました

root@e87f198ccc07:/tmp# osmium tags-filter kanto-latest.osm.pbf w/railway -o kanto-railway-latest.osm.pbf
[======================================================================] 100%
root@e87f198ccc07:/tmp# ls
kanto-latest.osm.pbf kanto-railway-latest.osm.pbf
root@e87f198ccc07:/tmp# osmium export kanto-railway-latest.osm.pbf -o kanto-railway-latest.json
root@e87f198ccc07:/tmp# ls
kanto-latest.osm.pbf kanto-railway-latest.json kanto-railway-latest.osm.pbf

2つ目のコマンドプロンプトを立ち上げて、dockerコンテナから、ローカルに、完成したjsonファイルを送り出します。

C:\Users\ebata\docker-osmium-tool>docker cp wonderful_lumiere:/tmp/kanto-railway-latest.json .

ローカルの方に変換されたファイルが戻ってきました。
C:\Users\ebata>ls
 Dockerfile   README.md   kanto-latest.osm.pbf   kanto-railway-latest.json   work
さて、これを、ドラッグして、QGISに放り込んで出てきた図です。

見事に鉄道だけが抽出されています。

しかし、重要なのはそこではありません。

芳賀・宇都宮LRT が、すでに表示されている ―― これは、今の私にとって、とてつもないインパクトなのです。

さて、今回はLRTを中心とした宇都宮市をカバーする地域の交通機関を切り出します。

【食べログ作れる? 】 OpenStreetMap から Osmiumで飲食店の位置情報を取得してGeoJSONに出力してみたを参照して、

$ osmium extract --bbox 左上の経度,左上の緯度,右下の経度, 右下の緯度 -o 出力したいファイル名.pbf 元のファイル名.osm.pbf

から、

$ osmium extract --bbox 139.76675736787732, 36.659949299138, 140.1596765021647, 36.47004171587971 -o utunomiya-lrt.osm.pbf kanto-latest.osm.pbf

として切り出してみます。

root@80e1a2d0dc02:/tmp# osmium extract --bbox 139.76675736787732, 36.659949299138, 140.1596765021647, 36.47004171587971 -o utunomiya-lrt.osm.pbf kanto-latest.osm.pbf
Error parsing command line: too many positional options have been specified on the command line

あれ、コンマと空白がダメだったかな?

root@80e1a2d0dc02:/tmp# osmium extract --bbox 139.76675736787732,36.659949299138,140.1596765021647,36.47004171587971 -o
utunomiya-lrt.osm.pbf kanto-latest.osm.pbf
Need LEFT < RIGHT and BOTTOM < TOP in --box/-b option.

うむ、 では、36.659949299138 と 36.47004171587971 を入れかえて、再度挑戦

root@80e1a2d0dc02:/tmp# osmium extract --bbox 139.76675736787732,36.47004171587971,140.1596765021647,36.659949299138 -o utunomiya-lrt.osm.pbf kanto-latest.osm.pbf
[======================================================================] 100%

成功したっぽいです。

root@80e1a2d0dc02:/tmp# osmium tags-filter utunomiya-lrt.osm.pbf w/railway -o utunomiya-railway-latest.osm.pbf
[======================================================================] 100%

root@80e1a2d0dc02:/tmp# osmium export utunomiya-railway-latest.osm.pbf -o utunomiya-railway-latest.json

では、ここから、2つ目のコマンドプロンプトから、

C:\Users\ebata\docker-osmium-tool>docker cp serene_shirley:/tmp/utunomiya-railway-latest.json .
C:\Users\ebata\docker-osmium-tool>docker cp serene_shirley:/tmp/utunomiya-lrt.osm.pbf .

としました。

utunomiya-railway-latest.json の表示結果はこちらです。

データ量も、関東全域と比較すると、11%程度になりました。


ところで、osmファイルと、osm.pbfファイルの違いって何だろう?