github.com/liucxer/courier@v1.7.1/h3/vertex_graph.go (about) 1 package h3 2 3 import ( 4 "math" 5 ) 6 7 type VertexNode struct { 8 from GeoCoord 9 to GeoCoord 10 next *VertexNode 11 } 12 13 type VertexGraph struct { 14 buckets []*VertexNode 15 numBuckets int 16 size int 17 res int 18 } 19 20 /** 21 * Initialize a new VertexGraph 22 * @param graph Graph to initialize 23 * @param numBuckets Number of buckets to include in the graph 24 * @param res Resolution of the hexagons whose vertices we're storing 25 */ 26 func initVertexGraph(graph *VertexGraph, numBuckets int, res int) { 27 if numBuckets > 0 { 28 graph.buckets = make([]*VertexNode, numBuckets) 29 } else { 30 graph.buckets = nil 31 } 32 33 graph.numBuckets = numBuckets 34 graph.size = 0 35 graph.res = res 36 } 37 38 /** 39 * Destroy a VertexGraph's sub-objects, freeing their memory. The caller is 40 * responsible for freeing memory allocated to the VertexGraph struct itself. 41 * @param graph Graph to destroy 42 */ 43 func destroyVertexGraph(graph *VertexGraph) { 44 node := firstVertexNode(graph); 45 for node != nil { 46 removeVertexNode(graph, node) 47 node = firstVertexNode(graph) 48 } 49 graph.buckets = nil 50 } 51 52 /** 53 * Get an integer hash for a Lat/Lon point, at a precision determined 54 * by the current hexagon resolution. 55 * TODO: Light testing suggests this might not be sufficient at resolutions 56 * finer than 10. Design a better hash function if performance and collisions 57 * seem to be an issue here. 58 * @param vertex Lat/Lon vertex to hash 59 * @param res Resolution of the hexagon the vertex belongs to 60 * @param numBuckets Number of buckets in the graph 61 * @return Integer hash 62 */ 63 func _hashVertex(vertex *GeoCoord, res int, numBuckets int) uint32 { 64 // Simple hash: Take the sum of the Lat and Lon with a precision level 65 // determined by the resolution, converted to int, modulo bucket count. 66 return uint32(math.Mod(math.Abs((vertex.Lat+vertex.Lon)*math.Pow(10, float64(15-res))), float64(numBuckets))) 67 } 68 69 func _initVertexNode(node *VertexNode, fromVtx *GeoCoord, toVtx *GeoCoord) { 70 node.from = *fromVtx 71 node.to = *toVtx 72 node.next = nil 73 } 74 75 /** 76 * Add a edge to the graph 77 * @param graph Graph to add node to 78 * @param fromVtx Start vertex 79 * @param toVtx End vertex 80 * @return Pointer to the new node 81 */ 82 func addVertexNode(graph *VertexGraph, fromVtx *GeoCoord, toVtx *GeoCoord) *VertexNode { 83 // Make the new node 84 node := &VertexNode{} 85 86 _initVertexNode(node, fromVtx, toVtx) 87 // Determine location 88 index := _hashVertex(fromVtx, graph.res, graph.numBuckets) 89 // Check whether there's an existing node in that spot 90 currentNode := graph.buckets[index] 91 92 if currentNode == nil { 93 // Set bucket to the new node 94 graph.buckets[index] = node 95 } else { 96 // Find the end of the list 97 for { 98 // Check the the edge we're adding doesn't already exist 99 if geoAlmostEqual(¤tNode.from, fromVtx) && geoAlmostEqual(¤tNode.to, toVtx) { 100 node = nil 101 // already exists, bail 102 return currentNode 103 } 104 105 if currentNode.next != nil { 106 currentNode = currentNode.next 107 } 108 109 if currentNode.next == nil { 110 break 111 } 112 } 113 // Add the new node to the end of the list 114 currentNode.next = node 115 } 116 117 graph.size++ 118 return node 119 } 120 121 /** 122 * Remove a node from the graph. The input node will be freed, and should 123 * not be used after removal. 124 * @param graph Graph to mutate 125 * @param node Node to remove 126 * @return 0 on success, 1 on failure (node not found) 127 */ 128 func removeVertexNode(graph *VertexGraph, node *VertexNode) int { 129 // Determine location 130 index := _hashVertex(&node.from, graph.res, graph.numBuckets) 131 currentNode := graph.buckets[index] 132 found := 0 133 134 if currentNode != nil { 135 if currentNode == node { 136 graph.buckets[index] = node.next 137 found = 1 138 } 139 // Look through the list 140 for found != 1 && currentNode.next != nil { 141 if currentNode.next == node { 142 // splice the node out 143 currentNode.next = node.next 144 found = 1 145 } 146 currentNode = currentNode.next 147 } 148 } 149 150 if found > 0 { 151 node = nil 152 graph.size-- 153 return 0 154 } 155 156 return 1 157 } 158 159 /** 160 * Find the Vertex node for a given edge, if it exists 161 * @param graph Graph to look in 162 * @param fromVtx Start vertex 163 * @param toVtx End vertex, or nil if we don't care 164 * @return Pointer to the vertex node, if found 165 */ 166 func findNodeForEdge(graph *VertexGraph, fromVtx *GeoCoord, toVtx *GeoCoord) *VertexNode { 167 // Determine location 168 index := _hashVertex(fromVtx, graph.res, graph.numBuckets) 169 // Check whether there's an existing node in that spot 170 node := graph.buckets[index] 171 172 // Look through the list and see if we find the edge 173 for node != nil { 174 if geoAlmostEqual(&node.from, fromVtx) && (toVtx == nil || geoAlmostEqual(&node.to, toVtx)) { 175 return node 176 } 177 node = node.next 178 } 179 // Iteration lookup fail 180 return nil 181 } 182 183 /** 184 * Find a Vertex node starting at the given vertex 185 * @param graph Graph to look in 186 * @param fromVtx Start vertex 187 * @return Pointer to the vertex node, if found 188 */ 189 func findNodeForVertex(graph *VertexGraph, fromVtx *GeoCoord) *VertexNode { 190 return findNodeForEdge(graph, fromVtx, nil) 191 } 192 193 /** 194 * Get the next vertex node in the graph. 195 * @param graph Graph to iterate 196 * @return Vertex node, or nil if at the end 197 */ 198 func firstVertexNode(graph *VertexGraph) *VertexNode { 199 var node *VertexNode 200 201 currentIndex := 0 202 203 for node == nil { 204 if currentIndex < graph.numBuckets { 205 // find the first node in the next bucket 206 node = graph.buckets[currentIndex] 207 } else { 208 // end of iteration 209 return nil 210 } 211 currentIndex++ 212 } 213 214 return node 215 }