github.com/LdDl/ch@v1.7.8/isochrones.go (about) 1 package ch 2 3 import ( 4 "container/heap" 5 "fmt" 6 ) 7 8 // Isochrones Returns set of vertices and corresponding distances restricted by maximum travel cost for source vertex 9 // source - source vertex (user defined label) 10 // maxCost - restriction on travel cost for breadth search 11 // See ref. https://wiki.openstreetmap.org/wiki/Isochrone and https://en.wikipedia.org/wiki/Isochrone_map 12 // Note: implemented breadth-first searching path algorithm does not guarantee shortest pathes to reachable vertices (until all edges have cost 1.0). See ref: https://en.wikipedia.org/wiki/Breadth-first_search 13 // Note: result for estimated costs could be also inconsistent due nature of data structure 14 func (graph *Graph) Isochrones(source int64, maxCost float64) (map[int64]float64, error) { 15 var ok bool 16 if source, ok = graph.mapping[source]; !ok { 17 return nil, fmt.Errorf("no such source") 18 } 19 Q := &minHeap{} 20 heap.Init(Q) 21 distance := make(map[int64]float64, len(graph.Vertices)) 22 Q.Push(&minHeapVertex{id: source, distance: 0}) 23 visit := make(map[int64]bool) 24 for Q.Len() != 0 { 25 next := heap.Pop(Q).(*minHeapVertex) 26 visit[next.id] = true 27 if next.distance <= maxCost { 28 distance[graph.Vertices[next.id].Label] = next.distance 29 vertexList := graph.Vertices[next.id].outIncidentEdges 30 for i := range vertexList { 31 neighbor := vertexList[i].vertexID 32 if v1, ok1 := graph.shortcuts[next.id]; ok1 { 33 if _, ok2 := v1[neighbor]; ok2 { 34 // Ignore shortcut 35 continue 36 } 37 } 38 target := vertexList[i].vertexID 39 cost := vertexList[i].weight 40 alt := distance[graph.Vertices[next.id].Label] + cost 41 if visit[target] { 42 continue 43 } 44 Q.Push(&minHeapVertex{id: target, distance: alt}) 45 } 46 } 47 } 48 return distance, nil 49 }