ノードの切断を発生させず、ノード10~ノード20と、ノード30~ノード40を無効とするダイクストラ計算を行うGo言語プログラム

2023年9月30日

pgr_dijkstra()で、ダイクストラの順番を壊さずにルートの座標を得る方法(getDijkstraPath())

ノードの切断を発生させず、ノード10~ノード20と、ノード30~ノード40を回避するダイクストラ計算を行うPostGISのクエリー

をベースとしたGo言語のプログラムになります。

// C:\Users\ebata\tomioka3B\src\others\main23.go
package main

import (
	"database/sql"
	"fmt"
	"log"
	"m/src/ldarp"
	"strconv"

	_ "github.com/lib/pq"
)

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

	/*
		以下のldarp.LocInfoの内容は、
		type LocInfo struct {
			Lon    float64
			Lat    float64
			Source int
		}
		となっています。
		このプログラムファイルでは、上記を定義して、ldarp.LocInfoを、LocInfoと変換すれば動きます

		ただ、前提とするpostgresqlのpostGISが取り扱うデータベースは、
		mapconfig_for_cars.xml をベースとして構築しているものになりますので、ご留意下さい
		https://github.com/pgRouting/osm2pgrouting/blob/main/mapconfig_for_cars.xml
	*/

	var a_Point, b_Point ldarp.LocInfo

	a_Point.Source = 1  // 出発ノード
	b_Point.Source = 50 // 到着ノード

	var del_start1, del_end1, del_start2, del_end2 ldarp.LocInfo
	//var del_start3, del_end3 ldarp.LocInfo

	del_start1.Source = 10 // 1つ目の無効としたいノード番号(開始番号)
	del_end1.Source = 20   // 1つ目の無効としたいノード番号(終了番号)
	del_start2.Source = 30 // 2つ目の無効としたいノード番号(開始番号)
	del_end2.Source = 40   // 2つ目の無効としたいノード番号(開始番号)
	//del_start3.Source = 30
	//del_end3.Source = 35

	array1 := []ldarp.LocInfo{del_start1, del_end1} // 上記の無効としたいノード番号を登録する
	array2 := []ldarp.LocInfo{del_start2, del_end2} // 上記の無効としたいノード番号を登録する
	//array3 := []ldarp.LocInfo{del_start3, del_end3}

	//delSourceArray := [][]ldarp.LocInfo{array1, array2, array3}
	delSourceArray := [][]ldarp.LocInfo{array1, array2} // 登録したノード番号をarraysの変数に纏める
	//delSourceArray = [][]ldarp.LocInfo{}

	// arrays変数への挿入
	//for _, arr := range arrays {
	//	delSourceArray = append(delSourceArray, arr)
	//}

	// fmt.Println(delSourceArray)

	path, dist := getDijkstraPath(db, a_Point, b_Point, delSourceArray)

	fmt.Println("path:", path)
	fmt.Println("dist:", dist)

}

// 江端再修正版(2023/09/29)
/*
出発ノードと到着ノードと、通過したくないノード(無効したいノード)を登録して、ダイクストラ計算によるルートを選ぶ
*/

func getDijkstraPath(dbMap *sql.DB, locInfoStart, locInfoGoal ldarp.LocInfo, delSourceArray [][]ldarp.LocInfo) ([]ldarp.LocInfo, float64) {
	log.Println("getDijkstraPath", locInfoStart, locInfoGoal)

	sql_str := "" // SQL文を作る文字列の初期値

	// delSourceArray の解析
	fmt.Println("delSourceArray:", delSourceArray)

	if len(delSourceArray) != 0 { // delSourceArrayに要素が入っていれば

		// forとrangeを使用して各要素のSource値を取得
		sql_str = "WHERE "

		for _, arr := range delSourceArray {
			/*
				for _, loc := range arr {
					fmt.Printf("Sourceの値: %d\n", loc.Source)
				}
			*/
			fmt.Println(arr[0].Source, arr[1].Source)
			//str += "( source NOT BETWEEN " + arr[0].Source + " AND " + arr[1].Source + " AND target NOT BETWEEN " + arr[0].Source + " AND " + arr[1].Source + ")"
			sql_str += "(source NOT BETWEEN " + strconv.Itoa(arr[0].Source) + " AND " + strconv.Itoa(arr[1].Source) + " AND target NOT BETWEEN " + strconv.Itoa(arr[0].Source) + " AND " + strconv.Itoa(arr[1].Source) + ") AND "
			//fmt.Println(str)
		}
		strlen := len(sql_str)
		sql_str = sql_str[:strlen-5] // ラストの" AND "の5文字を削除する
		//fmt.Println(str)
	}

	sql_str = "SELECT seq,source, target, x1, y1, x2, y2, agg_cost FROM pgr_dijkstra('SELECT gid as id, source, target, cost, reverse_cost FROM ways " + sql_str + "', $1::bigint , $2::bigint , directed:=false ) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq"

	//fmt.Println(sql_str)

	var path []ldarp.LocInfo // 経路 (返り値の一つ目)
	var totalDistanceKm float64

	rowsDijkstra, errDijkstra := dbMap.Query(
		//"SELECT seq,source, target, x1, y1, x2, y2, agg_cost FROM pgr_dijkstra('SELECT gid as id, source, target, length_m as cost FROM ways', $1::bigint , $2::bigint , directed:=false ) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq",
		//"SELECT seq,source, target, x1, y1, x2, y2, agg_cost FROM pgr_dijkstra('SELECT gid as id, source, target, length_m as cost FROM ways WHERE (source NOT BETWEEN 10 AND 20 AND target NOT BETWEEN 10 AND 20) AND (source NOT BETWEEN 30 AND 40 AND target NOT BETWEEN 30 AND 40)', $1::bigint , $2::bigint , directed:=false ) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq",
		sql_str,
		locInfoStart.Source,
		locInfoGoal.Source)

	if errDijkstra != nil {
		log.Fatal(errDijkstra)
	}
	defer rowsDijkstra.Close()

	var agg_cost float64

	var loc ldarp.LocInfo
	var x1, y1, x2, y2 float64
	var seq int
	var target int
	var source int

	isFirstCheck := true
	isSourceCheck := true

	for rowsDijkstra.Next() {

		// まずnodeを読む
		if err := rowsDijkstra.Scan(&seq, &source, &target, &x1, &y1, &x2, &y2, &agg_cost); err != nil {
			fmt.Println(err)
		}

		// 最初の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 ldarp.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)
	}

	// ラストノードだけはsourceとtargetを引っくり返す
	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
	}

	path = append(path, loc)

	totalDistanceKm = agg_cost / 1000.0
	return path, totalDistanceKm
}

2023年9月30日2023,江端さんの技術メモ

Posted by ebata