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(&currentNode.from, fromVtx) && geoAlmostEqual(&currentNode.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  }