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
}