github.com/liucxer/courier@v1.7.1/h3/h3_uni_edge.go (about)

     1  package h3
     2  
     3  /**
     4   * Returns whether or not the provided H3Indexes are neighbors.
     5   * @param origin The origin H3 index.
     6   * @param destination The destination H3 index.
     7   * @return 1 if the indexes are neighbors, 0 otherwise;
     8   */
     9  func h3IndexesAreNeighbors(origin H3Index, destination H3Index) int {
    10  	// Make sure they're hexagon indexes
    11  	if H3_GET_MODE(origin) != H3_HEXAGON_MODE ||
    12  		H3_GET_MODE(destination) != H3_HEXAGON_MODE {
    13  		return 0
    14  	}
    15  
    16  	// Hexagons cannot be neighbors with themselves
    17  	if origin == destination {
    18  		return 0
    19  	}
    20  
    21  	// Only hexagons in the same resolution can be neighbors
    22  	if H3_GET_RESOLUTION(origin) != H3_GET_RESOLUTION(destination) {
    23  		return 0
    24  	}
    25  
    26  	// H3 Indexes that share the same parent are very likely to be neighbors
    27  	// Child 0 is neighbor with all of its parent's 'offspring', the other
    28  	// children are neighbors with 3 of the 7 children. So a simple comparison
    29  	// of origin and destination parents and then a lookup table of the children
    30  	// is a super-cheap way to possibly determine they are neighbors.
    31  	parentRes := H3_GET_RESOLUTION(origin) - 1
    32  	if parentRes > 0 && (h3ToParent(origin, parentRes) ==
    33  		h3ToParent(destination, parentRes)) {
    34  		originResDigit := H3_GET_INDEX_DIGIT(origin, parentRes+1)
    35  		destinationResDigit := H3_GET_INDEX_DIGIT(destination, parentRes+1)
    36  		if originResDigit == CENTER_DIGIT ||
    37  			destinationResDigit == CENTER_DIGIT {
    38  			return 1
    39  		}
    40  		// These sets are the relevant neighbors in the clockwise
    41  		// and counter-clockwise
    42  		neighborSetClockwise := []Direction{
    43  			CENTER_DIGIT,
    44  			JK_AXES_DIGIT,
    45  			IJ_AXES_DIGIT,
    46  			J_AXES_DIGIT,
    47  			IK_AXES_DIGIT,
    48  			K_AXES_DIGIT,
    49  			I_AXES_DIGIT,
    50  		}
    51  
    52  		neighborSetCounterclockwise := []Direction{
    53  			CENTER_DIGIT, IK_AXES_DIGIT, JK_AXES_DIGIT, K_AXES_DIGIT,
    54  			IJ_AXES_DIGIT, I_AXES_DIGIT, J_AXES_DIGIT}
    55  
    56  		if neighborSetClockwise[originResDigit] == destinationResDigit || neighborSetCounterclockwise[originResDigit] ==
    57  			destinationResDigit {
    58  			return 1
    59  		}
    60  	}
    61  
    62  	// Otherwise, we have to determine the neighbor relationship the "hard" way.
    63  	neighborRing := make([]H3Index, 7)
    64  	kRing(origin, 1, neighborRing)
    65  	for i := 0; i < 7; i++ {
    66  		if neighborRing[i] == destination {
    67  			return 1
    68  		}
    69  	}
    70  
    71  	// Made it here, they definitely aren't neighbors
    72  	return 0
    73  }
    74  
    75  /**
    76   * Returns a unidirectional edge H3 index based on the provided origin and
    77   * destination
    78   * @param origin The origin H3 hexagon index
    79   * @param destination The destination H3 hexagon index
    80   * @return The unidirectional edge H3Index, or 0 on failure.
    81   */
    82  func getH3UnidirectionalEdge(origin H3Index, destination H3Index) H3Index {
    83  	// Short-circuit and return an invalid index value if they are not neighbors
    84  	if h3IndexesAreNeighbors(origin, destination) == 0 {
    85  		return H3_INVALID_INDEX
    86  	}
    87  
    88  	// Otherwise, determine the IJK direction from the origin to the destination
    89  	output := origin
    90  	H3_SET_MODE(&output, H3_UNIEDGE_MODE)
    91  
    92  	// Checks each neighbor, in order, to determine which direction the
    93  	// destination neighbor is located. Skips CENTER_DIGIT since that
    94  	// would be this index.
    95  	var neighbor H3Index
    96  	for direction := K_AXES_DIGIT; direction < NUM_DIGITS; direction++ {
    97  		rotations := 0
    98  		neighbor = h3NeighborRotations(origin, direction, &rotations)
    99  		if neighbor == destination {
   100  			H3_SET_RESERVED_BITS(&output, int(direction))
   101  			return output
   102  		}
   103  	}
   104  
   105  	// This should be impossible, return an invalid H3Index in this case;
   106  	return H3_INVALID_INDEX // LCOV_EXCL_LINE
   107  }
   108  
   109  /**
   110   * Returns the origin hexagon from the unidirectional edge H3Index
   111   * @param edge The edge H3 index
   112   * @return The origin H3 hexagon index
   113   */
   114  func getOriginH3IndexFromUnidirectionalEdge(edge H3Index) H3Index {
   115  	if H3_GET_MODE(edge) != H3_UNIEDGE_MODE {
   116  		return H3_INVALID_INDEX
   117  	}
   118  	origin := edge
   119  	H3_SET_MODE(&origin, H3_HEXAGON_MODE)
   120  	H3_SET_RESERVED_BITS(&origin, 0)
   121  	return origin
   122  }
   123  
   124  /**
   125   * Returns the destination hexagon from the unidirectional edge H3Index
   126   * @param edge The edge H3 index
   127   * @return The destination H3 hexagon index
   128   */
   129  func getDestinationH3IndexFromUnidirectionalEdge(edge H3Index) H3Index {
   130  	if H3_GET_MODE(edge) != H3_UNIEDGE_MODE {
   131  		return H3_INVALID_INDEX
   132  	}
   133  	direction := H3_GET_RESERVED_BITS(edge)
   134  	rotations := 0
   135  	destination := h3NeighborRotations(getOriginH3IndexFromUnidirectionalEdge(edge), Direction(direction), &rotations)
   136  	return destination
   137  }
   138  
   139  /**
   140   * Determines if the provided H3Index is a valid unidirectional edge index
   141   * @param edge The unidirectional edge H3Index
   142   * @return 1 if it is a unidirectional edge H3Index, otherwise 0.
   143   */
   144  func h3UnidirectionalEdgeIsValid(edge H3Index) bool {
   145  	if H3_GET_MODE(edge) != H3_UNIEDGE_MODE {
   146  		return false
   147  	}
   148  
   149  	neighborDirection := H3_GET_RESERVED_BITS(edge)
   150  	if Direction(neighborDirection) <= CENTER_DIGIT || Direction(neighborDirection) >= NUM_DIGITS {
   151  		return false
   152  	}
   153  
   154  	origin := getOriginH3IndexFromUnidirectionalEdge(edge)
   155  	if h3IsPentagon(origin) && Direction(neighborDirection) == K_AXES_DIGIT {
   156  		return false
   157  	}
   158  
   159  	return h3IsValid(origin)
   160  }
   161  
   162  /**
   163   * Returns the origin, destination pair of hexagon IDs for the given edge ID
   164   * @param edge The unidirectional edge H3Index
   165   * @param originDestination Pointer to memory to store origin and destination
   166   * IDs
   167   */
   168  func getH3IndexesFromUnidirectionalEdge(edge H3Index, originDestination []H3Index) {
   169  	originDestination[0] = getOriginH3IndexFromUnidirectionalEdge(edge)
   170  	originDestination[1] = getDestinationH3IndexFromUnidirectionalEdge(edge)
   171  }
   172  
   173  /**
   174   * Provides all of the unidirectional edges from the current H3Index.
   175   * @param origin The origin hexagon H3Index to find edges for.
   176   * @param edges The memory to store all of the edges inside.
   177   */
   178  func getH3UnidirectionalEdgesFromHexagon(origin H3Index, edges []H3Index) {
   179  	// Determine if the origin is a pentagon and special treatment needed.
   180  	isPentagon := h3IsPentagon(origin)
   181  
   182  	// This is actually quite simple. Just modify the bits of the origin
   183  	// slightly for each direction, except the 'k' direction in pentagons,
   184  	// which is zeroed.
   185  	for i := 0; i < 6; i++ {
   186  		if isPentagon && i == 0 {
   187  			edges[i] = H3_INVALID_INDEX
   188  		} else {
   189  			edges[i] = origin
   190  			H3_SET_MODE(&edges[i], H3_UNIEDGE_MODE)
   191  			H3_SET_RESERVED_BITS(&edges[i], i+1)
   192  		}
   193  	}
   194  }
   195  
   196  /**
   197   * Whether the given coordinate has a matching vertex in the given geo boundary.
   198   * @param  vertex   Coordinate to check
   199   * @param  boundary Geo boundary to look in
   200   * @return          Whether a match was found
   201   */
   202  func _hasMatchingVertex(vertex *GeoCoord, boundary *GeoBoundary) bool {
   203  	for i := 0; i < boundary.numVerts; i++ {
   204  		if geoAlmostEqualThreshold(vertex, &boundary.Verts[i], 0.000001) {
   205  			return true
   206  		}
   207  	}
   208  	return false
   209  }
   210  
   211  /**
   212   * Provides the coordinates defining the unidirectional edge.
   213   * @param edge The unidirectional edge H3Index
   214   * @param gb The geoboundary object to store the edge coordinates.
   215   */
   216  func getH3UnidirectionalEdgeBoundary(edge H3Index, gb *GeoBoundary) {
   217  	// TODO: More efficient solution :)
   218  	origin := GeoBoundary{}
   219  	destination := GeoBoundary{}
   220  	postponedVertex := GeoCoord{}
   221  	hasPostponedVertex := false
   222  
   223  	h3ToGeoBoundary(getOriginH3IndexFromUnidirectionalEdge(edge), &origin)
   224  	h3ToGeoBoundary(getDestinationH3IndexFromUnidirectionalEdge(edge), &destination)
   225  
   226  	k := 0
   227  	for i := 0; i < origin.numVerts; i++ {
   228  		if _hasMatchingVertex(&origin.Verts[i], &destination) {
   229  			// If we are on vertex 0, we need to handle the case where it's the
   230  			// end of the edge, not the beginning.
   231  			if i == 0 && !_hasMatchingVertex(&origin.Verts[i+1], &destination) {
   232  				postponedVertex = origin.Verts[i]
   233  				hasPostponedVertex = true
   234  			} else {
   235  				gb.Verts[k] = origin.Verts[i]
   236  				k++
   237  			}
   238  		}
   239  	}
   240  	// If we postponed adding the last vertex, add it now
   241  	if hasPostponedVertex {
   242  		gb.Verts[k] = postponedVertex
   243  		k++
   244  	}
   245  	gb.numVerts = k
   246  }