江端がずっとメンテナンスしているgetDijkstraPath関数は、costを変更することによって、ダイクストラ計算を引き曲げているので、距離が正確に出せない。
そこで、agg_costを使わずに、agg_length_mというものを作って、強制的に距離を作り出すようにコードを変更した。
func getDijkstraPath(dbMap *sql.DB, locInfoStart, locInfoGoal LocInfo) ([]LocInfo, float64) {
var path []LocInfo // 経路 (返り値の一つ目)
var totalDistanceKm float64
// 例外処理 locInfoStart.source == locInfoGoal.source の場合
if locInfoStart.Source == locInfoGoal.Source {
source := locInfoStart.Source
// SQLクエリの作成
query := fmt.Sprintf(`
SELECT x1, y1
FROM ways
WHERE source = %d;
`, source)
// SQLクエリの実行
var x1, y1 float64
err := dbMap.QueryRow(query).Scan(&x1, &y1)
if err != nil {
fmt.Println("tools.go line 204 error occures")
//log.Fatal(err) // ここで止めたらリターンしなくなる
}
var loc LocInfo
loc.Source = source
loc.Lon = x1
loc.Lat = y1
path = append(path, loc)
totalDistanceKm = 0.0
return path, totalDistanceKm
}
// こちらは、cost, reverse_cost を使っている
query := `
SELECT seq, source, target, x1, y1, x2, y2, agg_cost, length_m
FROM pgr_dijkstra(
'SELECT gid as id, source, target, cost, reverse_cost FROM ways',
$1::bigint,
$2::bigint,
directed:=false
) a
INNER JOIN ways b ON (a.edge = b.gid)
ORDER BY seq
`
rowsDijkstra, errDijkstra := dbMap.Query(query, locInfoStart.Source, locInfoGoal.Source)
if errDijkstra != nil {
log.Fatal(errDijkstra)
os.Exit(1)
}
defer rowsDijkstra.Close()
var agg_cost float64
var length_m float64
agg_length_m := 0.0
var loc LocInfo
var x1, y1, x2, y2 float64
var seq int
var target int
var source int
isFirstCheck := true
isSourceCheck := true
count := 0
for rowsDijkstra.Next() {
// まずnodeを読む
if err := rowsDijkstra.Scan(&seq, &source, &target, &x1, &y1, &x2, &y2, &agg_cost, &length_m); err != nil {
fmt.Println(err)
}
agg_length_m += length_m
// fmt.Println("length_m", length_m, "agg_length_m", agg_length_m)
// 最初の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 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)
count++
}
// ラストノードだけは手入力 (ここは引っくり返す) (今、ここの部分は無視されている(いいのかな?))
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
}
// なんかprg_dijkstraが変な値を返す場合があるので、その対応(ロジカルではないが、パッチ的に対応)
if loc.Source == locInfoGoal.Source {
path = append(path, loc)
}
fmt.Println("count", count)
if count == 0 { // 1行のみの場合、ヌルになるという問題に対応するため、
loc.Source = locInfoGoal.Source
loc.Lon = locInfoGoal.Lon
loc.Lat = locInfoGoal.Lat
// 入力値の指定
source := locInfoStart.Source
target := locInfoGoal.Source
// SQLクエリの作成
query := fmt.Sprintf(`
SELECT length_m, x1, y1, x2, y2
FROM ways
WHERE source = %d AND target = %d;
`, source, target)
// SQLクエリの実行
var length float64
var x1, y1, x2, y2 float64
err := dbMap.QueryRow(query).Scan(&length, &x1, &y1, &x2, &y2)
if err != nil {
log.Println("First attempt failed. Retrying with swapped source and target.")
// 入れ替えたsourceとtargetの値でクエリを再実行
query = fmt.Sprintf(`
SELECT length_m, x1, y1, x2, y2
FROM ways
WHERE source = %d AND target = %d;
`, target, source)
err = dbMap.QueryRow(query).Scan(&length, &x1, &y1, &x2, &y2)
if err != nil {
//log.Fatal(err) // 諦める。とりあえず、エラーを返す
return nil, 0.0
}
}
// 結果の出力
fmt.Printf("length_m: %f\n", length)
fmt.Printf("source: %d\n", source)
fmt.Printf("target: %d\n", target)
fmt.Printf("x1: %f\n", x1)
fmt.Printf("y1: %f\n", y1)
fmt.Printf("x2: %f\n", x2)
fmt.Printf("y2: %f\n", y2)
if source == locInfoGoal.Source {
loc.Lon = x1
loc.Lat = y1
} else {
loc.Lon = x2
loc.Lat = y2
}
agg_cost = length
fmt.Println("loc", loc)
path = append(path, loc) // 354
} // if count == 0
//totalDistanceKm = agg_cost / 1000.0
totalDistanceKm = agg_length_m / 1000.0
return path, totalDistanceKm
}