Exemple #1
0
        /// <summary>
        /// Determines the cell boundary in spherical coordinates for an H3 index.
        /// </summary>
        /// <param name="h3"> The H3 index.</param>
        /// <param name="gb">The boundary of the H3 cell in spherical coordinates.</param>
        /// <!-- Based off 3.1.1 -->
        public static void h3ToGeoBoundary(H3Index h3, ref GeoBoundary gb)
        {
            FaceIJK fijk = new FaceIJK();

            _h3ToFaceIjk(h3, ref fijk);
            FaceIJK._faceIjkToGeoBoundary(
                ref fijk, H3_GET_RESOLUTION(h3),
                h3IsPentagon(h3), ref gb
                );
        }
Exemple #2
0
 /// <summary>
 /// Whether the given coordinate has a matching vertex in the given geo boundary.
 /// </summary>
 /// <param name="vertex">Coordinate to check</param>
 /// <param name="boundary">Geo boundary to look in</param>
 /// <returns>Whether a match was found</returns>
 /// <!-- Based off 3.1.1 -->
 public static bool _hasMatchingVertex(GeoCoord vertex, GeoBoundary boundary)
 {
     for (int i = 0; i < boundary.numVerts; i++)
     {
         if (GeoCoord.geoAlmostEqualThreshold(vertex, boundary.verts[i], 0.000001))
         {
             return(true);
         }
     }
     return(false);
 }
Exemple #3
0
        /// <summary>
        /// Returns the radius of a given hexagon in kilometers
        /// </summary>
        /// <param name="h3Index">Index of the hexagon</param>
        /// <returns>radius of hexagon in kilometers</returns>
        /// <!-- Based off 3.1.1 -->
        static double _hexRadiusKm(H3Index h3Index)
        {
            // There is probably a cheaper way to determine the radius of a
            // hexagon, but this way is conceptually simple
            GeoCoord    h3Center   = new GeoCoord();
            GeoBoundary h3Boundary = new GeoBoundary();

            H3Index.h3ToGeo(h3Index, ref h3Center);
            H3Index.h3ToGeoBoundary(h3Index, ref h3Boundary);
            return(GeoCoord._geoDistKm(h3Center, h3Boundary.verts));
        }
Exemple #4
0
            public GeoBoundary(Code.GeoBoundary cgb)
            {
                VertexCount = cgb.numVerts;
                List <GeoCoord> lgc = new List <GeoCoord>();

                foreach (var vertex in cgb.verts)
                {
                    lgc.Add(new GeoCoord(vertex));
                }

                Vertices = lgc.ToArray();
            }
Exemple #5
0
        public static GeoBoundary GetH3UnidirectionalEdgeBoundary(Code.H3Index edge)
        {
            Code.GeoBoundary gb = new Code.GeoBoundary();
            H3UniEdge.getH3UnidirectionalEdgeBoundary(edge, ref gb);
            var newVerts = gb.verts.Select(v => new GeoCoord(v)).ToArray();

            return(new GeoBoundary
            {
                VertexCount = gb.numVerts,
                Vertices = newVerts
            });
        }
Exemple #6
0
        /// <summary>
        /// Internal: Create a vertex graph from a set of hexagons. It is the
        /// responsibility of the caller to call destroyVertexGraph on the populated
        /// graph, otherwise the memory in the graph nodes will not be freed.
        /// </summary>
        ///
        /// <param name="h3Set">Set of hexagons</param>
        /// <param name="numHexes">Number of hexagons in the set</param>
        /// <param name="graph">Output graph</param>
        /// <!-- Based off 3.1.1 -->
        public static void h3SetToVertexGraph(ref List <H3Index> h3Set, int numHexes,
                                              ref VertexGraph graph)
        {
            GeoBoundary vertices   = new GeoBoundary();
            GeoCoord    fromVertex = new GeoCoord();
            GeoCoord    toVertex   = new GeoCoord();

            VertexGraph.VertexNode edge;
            if (numHexes < 1)
            {
                // We still need to init the graph, or calls to destroyVertexGraph will
                // fail
                graph = new VertexGraph(0, 0);
                return;
            }

            int       res        = H3Index.H3_GET_RESOLUTION(h3Set[0]);
            const int minBuckets = 6;
            // TODO: Better way to calculate/guess?
            int numBuckets = numHexes > minBuckets ? numHexes : minBuckets;

            graph = new VertexGraph(numBuckets, res);

            // Iterate through every hexagon
            for (int i = 0; i < numHexes; i++)
            {
                H3Index.h3ToGeoBoundary(h3Set[i], ref vertices);
                // iterate through every edge
                for (int j = 0; j < vertices.numVerts; j++)
                {
                    fromVertex = new GeoCoord(vertices.verts[j].lat, vertices.verts[j].lon);
                    //fromVtx = vertices.verts[j];
                    int idx = (j + 1) % vertices.numVerts;
                    toVertex = new GeoCoord(vertices.verts[idx].lat, vertices.verts[idx].lon);
                    //toVtx = vertices.verts[(j + 1) % vertices.numVerts];
                    // If we've seen this edge already, it will be reversed
                    edge = VertexGraph.findNodeForEdge(ref graph, toVertex, fromVertex);
                    if (edge != null)
                    {
                        // If we've seen it, drop it. No edge is shared by more than 2
                        // hexagons, so we'll never see it again.
                        VertexGraph.removeVertexNode(ref graph, ref edge);
                    }
                    else
                    {
                        // Add a new node for this edge
                        VertexGraph.addVertexNode(ref graph, fromVertex, toVertex);
                    }
                }
            }
        }
Exemple #7
0
        public static Api.GeoBoundary H3ToGeoBoundary(Code.H3Index h3)
        {
            var blank = new Code.GeoBoundary();

            Code.H3Index.h3ToGeoBoundary(h3, ref blank);

            var newVerts = blank.verts.Select(v => new Api.GeoCoord(v)).ToArray();

            return(new GeoBoundary
            {
                VertexCount = blank.numVerts,
                Vertices = newVerts
            });
        }
Exemple #8
0
        /// <summary>
        /// Provides the coordinates defining the unidirectional edge.
        /// </summary>
        /// <param name="edge">The unidirectional edge H3Index</param>
        /// <param name="gb">
        /// The geoboundary object to store the edge coordinates.
        /// </param>
        /// <!-- Based off 3.1.1 -->
        public static void getH3UnidirectionalEdgeBoundary(H3Index edge, ref GeoBoundary gb)
        {
            // TODO: More efficient solution :)
            GeoBoundary origin             = new GeoBoundary();
            GeoBoundary destination        = new GeoBoundary();
            GeoCoord    postponedVertex    = new GeoCoord();
            bool        hasPostponedVertex = false;

            H3Index.h3ToGeoBoundary(getOriginH3IndexFromUnidirectionalEdge(edge), ref origin);
            H3Index.h3ToGeoBoundary(getDestinationH3IndexFromUnidirectionalEdge(edge), ref destination);

            int k = 0;

            for (int i = 0; i < origin.numVerts; i++)
            {
                if (_hasMatchingVertex(origin.verts[i], destination))
                {
                    // If we are on vertex 0, we need to handle the case where it's the
                    // end of the edge, not the beginning.
                    if (i == 0 &&
                        !_hasMatchingVertex(origin.verts[i + 1], destination))
                    {
                        postponedVertex    = origin.verts[i];
                        hasPostponedVertex = true;
                    }
                    else
                    {
                        gb.verts[k] = origin.verts[i];
                        k++;
                    }
                }
            }

            // If we postponed adding the last vertex, add it now
            if (hasPostponedVertex)
            {
                gb.verts[k] = postponedVertex;
                k++;
            }
            gb.numVerts = k;
        }
Exemple #9
0
        /// <summary>
        /// Generates the cell boundary in spherical coordinates for a cell given by a
        /// FaceIJK address at a specified resolution.
        /// </summary>
        /// <param name="h">The FaceIJK address of the cell.</param>
        /// <param name="res">The H3 resolution of the cell.</param>
        /// <param name="isPentagon">Whether or not the cell is a pentagon.</param>
        /// <param name="g">The spherical coordinates of the cell boundary.</param>
        /// <!-- Based off 3.1.1 -->
        public static void _faceIjkToGeoBoundary(ref FaceIJK h, int res, int isPentagon, ref GeoBoundary g)
        {
            if (isPentagon > 0)
            {
                _faceIjkPentToGeoBoundary(ref h, res, ref g);
                return;
            }

            // the vertexes of an origin-centered cell in a Class II resolution on a
            // substrate grid with aperture sequence 33r. The aperture 3 gets us the
            // vertices, and the 3r gets us back to Class II.
            // vertices listed ccw from the i-axes
            CoordIJK[] vertsCII =
            {
                new CoordIJK {
                    i = 2, j = 1, k = 0
                },                                  // 0
                new CoordIJK {
                    i = 1, j = 2, k = 0
                },                                  // 1
                new CoordIJK {
                    i = 0, j = 2, k = 1
                },                                  // 2
                new CoordIJK {
                    i = 0, j = 1, k = 2
                },                                  // 3
                new CoordIJK {
                    i = 1, j = 0, k = 2
                },                                  // 4
                new CoordIJK {
                    i = 2, j = 0, k = 1
                }                                  // 5
            };

            // the vertexes of an origin-centered cell in a Class III resolution on a
            // substrate grid with aperture sequence 33r7r. The aperture 3 gets us the
            // vertices, and the 3r7r gets us to Class II.
            // vertices listed ccw from the i-axes
            CoordIJK[] vertsCIII =
            {
                new CoordIJK {
                    i = 5, j = 4, k = 0
                },                                  // 0
                new CoordIJK {
                    i = 1, j = 5, k = 0
                },                                  // 1
                new CoordIJK {
                    i = 0, j = 5, k = 4
                },                                  // 2
                new CoordIJK {
                    i = 0, j = 1, k = 5
                },                                  // 3
                new CoordIJK {
                    i = 4, j = 0, k = 5
                },                                  // 4
                new CoordIJK {
                    i = 5, j = 0, k = 1
                }                                  // 5
            };

            // get the correct set of substrate vertices for this resolution
            CoordIJK[] verts;
            if (H3Index.isResClassIII(res))
            {
                verts = vertsCIII;
            }
            else
            {
                verts = vertsCII;
            }

            // adjust the center point to be in an aperture 33r substrate grid
            // these should be composed for speed
            FaceIJK centerIJK = new FaceIJK(h.face, new CoordIJK(h.coord.i, h.coord.j, h.coord.k));

            CoordIJK._downAp3(ref centerIJK.coord);
            CoordIJK._downAp3r(ref centerIJK.coord);

            // if res is Class III we need to add a cw aperture 7 to get to
            // icosahedral Class II
            int adjRes = res;

            if (H3Index.isResClassIII(res))
            {
                CoordIJK._downAp7r(ref centerIJK.coord);
                adjRes++;
            }

            // The center point is now in the same substrate grid as the origin
            // cell vertices. Add the center point substate coordinates
            // to each vertex to translate the vertices to that cell.
            FaceIJK[] fijkVerts = new FaceIJK[Constants.NUM_HEX_VERTS];
            for (int v = 0; v < Constants.NUM_HEX_VERTS; v++)
            {
                fijkVerts[v]      = new FaceIJK();
                fijkVerts[v].face = centerIJK.face;
                CoordIJK._ijkAdd(centerIJK.coord, verts[v], ref fijkVerts[v].coord);
                CoordIJK._ijkNormalize(ref fijkVerts[v].coord);
            }

            // convert each vertex to lat/lon
            // adjust the face of each vertex as appropriate and introduce
            // edge-crossing vertices as needed
            g.numVerts = 0;
            int lastFace    = -1;
            int lastOverage = 0; // 0: none; 1: edge; 2: overage

            for (int vert = 0; vert < Constants.NUM_HEX_VERTS + 1; vert++)
            {
                int v = vert % Constants.NUM_HEX_VERTS;

                FaceIJK fijk = new FaceIJK
                               (
                    fijkVerts[v].face,
                    new CoordIJK(fijkVerts[v].coord.i, fijkVerts[v].coord.j, fijkVerts[v].coord.k)
                               );

                int pentLeading4 = 0;
                int overage      = _adjustOverageClassII(ref fijk, adjRes, pentLeading4, 1);

                /*
                 * Check for edge-crossing. Each face of the underlying icosahedron is a
                 * different projection plane. So if an edge of the hexagon crosses an
                 * icosahedron edge, an additional vertex must be introduced at that
                 * intersection point. Then each half of the cell edge can be projected
                 * to geographic coordinates using the appropriate icosahedron face
                 * projection. Note that Class II cell edges have vertices on the face
                 * edge, with no edge line intersections.
                 */
                if (H3Index.isResClassIII(res) && vert > 0 && fijk.face != lastFace &&
                    lastOverage != 1)
                {
                    // find hex2d of the two vertexes on original face
                    int   lastV   = (v + 5) % Constants.NUM_HEX_VERTS;
                    Vec2d orig2d0 = new Vec2d();
                    CoordIJK._ijkToHex2d(fijkVerts[lastV].coord, ref orig2d0);

                    Vec2d orig2d1 = new Vec2d();
                    CoordIJK._ijkToHex2d(fijkVerts[v].coord, ref orig2d1);

                    // find the appropriate icosa face edge vertexes
                    int   maxDim = maxDimByCIIres[adjRes];
                    Vec2d v0     = new Vec2d(3.0 * maxDim, 0.0);
                    Vec2d v1     = new Vec2d(-1.5 * maxDim, 3.0 * Constants.M_SQRT3_2 * maxDim);
                    Vec2d v2     = new Vec2d(-1.5 * maxDim, -3.0 * Constants.M_SQRT3_2 * maxDim);

                    int   face2 = lastFace == centerIJK.face ? fijk.face : lastFace;
                    Vec2d edge0 = new Vec2d();
                    Vec2d edge1 = new Vec2d();
                    switch (adjacentFaceDir[centerIJK.face, face2])
                    {
                    case IJ:
                        edge0 = v0;
                        edge1 = v1;
                        break;

                    case JK:
                        edge0 = v1;
                        edge1 = v2;
                        break;

                    case KI:
                    default:
                        if (adjacentFaceDir[centerIJK.face, face2] != KI)
                        {
                            throw new Exception("Default failure in _faceIjkToGeoBoundary");
                        }

                        edge0 = v2;
                        edge1 = v0;
                        break;
                    }

                    // find the intersection and add the lat/lon point to the result
                    Vec2d inter = new Vec2d();
                    Vec2d._v2dIntersect(orig2d0, orig2d1, edge0, edge1, ref inter);

                    /*
                     * If a point of intersection occurs at a hexagon vertex, then each
                     * adjacent hexagon edge will lie completely on a single icosahedron
                     * face, and no additional vertex is required.
                     */
                    bool isIntersectionAtVertex =
                        Vec2d._v2dEquals(orig2d0, inter) || Vec2d._v2dEquals(orig2d1, inter);
                    if (!isIntersectionAtVertex)
                    {
                        var temp_verts = g.verts[g.numVerts];
                        _hex2dToGeo(ref inter, centerIJK.face, adjRes, 1, ref temp_verts);
                        g.verts[g.numVerts] = temp_verts;
                        g.numVerts++;
                        Debug.WriteLine(string.Format("!IsIntersection {0}", g.numVerts));
                    }
                }

                // convert vertex to lat/lon and add to the result
                // vert == NUM_HEX_VERTS is only used to test for possible intersection
                // on last edge
                if (vert < Constants.NUM_HEX_VERTS)
                {
                    Vec2d vec = new Vec2d();
                    CoordIJK._ijkToHex2d(fijk.coord, ref vec);
                    var temp_verts = g.verts[g.numVerts];
                    _hex2dToGeo(ref vec, fijk.face, adjRes, 1, ref temp_verts);
                    g.verts[g.numVerts] = temp_verts;
                    g.numVerts++;
                }

                lastFace    = fijk.face;
                lastOverage = overage;
            }
        }
Exemple #10
0
        /// <summary>
        /// Generates the cell boundary in spherical coordinates for a pentagonal cell
        /// given by a FaceIJK address at a specified resolution.
        /// </summary>
        /// <param name="h">The FaceIJK address of the pentagonal cell.</param>
        /// <param name="res">The H3 resolution of the cell.</param>
        /// <param name="g">The spherical coordinates of the cell boundary.</param>
        /// <!-- Based off 3.1.1 -->
        public static void _faceIjkPentToGeoBoundary(ref FaceIJK h, int res, ref GeoBoundary g)
        {
            // the vertexes of an origin-centered pentagon in a Class II resolution on a
            // substrate grid with aperture sequence 33r. The aperture 3 gets us the
            // vertices, and the 3r gets us back to Class II.
            // vertices listed ccw from the i-axes
            CoordIJK[] vertsCII =
            {
                new CoordIJK(2, 1, 0), // 0
                new CoordIJK(1, 2, 0), // 1
                new CoordIJK(0, 2, 1), // 2
                new CoordIJK(0, 1, 2), // 3
                new CoordIJK(1, 0, 2)  // 4
            };

            // the vertexes of an origin-centered pentagon in a Class III resolution on
            // a substrate grid with aperture sequence 33r7r. The aperture 3 gets us the
            // vertices, and the 3r7r gets us to Class II. vertices listed ccw from the
            // i-axes
            CoordIJK[] vertsCIII =
            {
                new CoordIJK(5, 4, 0), // 0
                new CoordIJK(1, 5, 0), // 1
                new CoordIJK(0, 5, 4), // 2
                new CoordIJK(0, 1, 5), // 3
                new CoordIJK(4, 0, 5)  // 4
            };

            // get the correct set of substrate vertices for this resolution
            List <CoordIJK> verts = new List <CoordIJK>();

            verts = H3Index.isResClassIII(res)
                ? vertsCIII.ToList()
                : vertsCII.ToList();

            // adjust the center point to be in an aperture 33r substrate grid
            // these should be composed for speed
            FaceIJK centerIJK = new FaceIJK();

            centerIJK.face  = h.face;
            centerIJK.coord = new CoordIJK(h.coord.i, h.coord.j, h.coord.k);
            CoordIJK._downAp3(ref centerIJK.coord);
            CoordIJK._downAp3r(ref centerIJK.coord);

            // if res is Class III we need to add a cw aperture 7 to get to
            // icosahedral Class II
            int adjRes = res;

            if (H3Index.isResClassIII(res))
            {
                CoordIJK._downAp7r(ref centerIJK.coord);
                adjRes++;
            }

            // The center point is now in the same substrate grid as the origin
            // cell vertices. Add the center point substate coordinates
            // to each vertex to translate the vertices to that cell.
            FaceIJK[] fijkVerts = new FaceIJK[Constants.NUM_PENT_VERTS];
            for (int v = 0; v < Constants.NUM_PENT_VERTS; v++)
            {
                fijkVerts[v]      = new FaceIJK();
                fijkVerts[v].face = centerIJK.face;
                CoordIJK._ijkAdd(centerIJK.coord, verts[v], ref fijkVerts[v].coord);
                CoordIJK._ijkNormalize(ref fijkVerts[v].coord);
            }

            // convert each vertex to lat/lon
            // adjust the face of each vertex as appropriate and introduce
            // edge-crossing vertices as needed
            g.numVerts = 0;
            for (int i = 0; i < g.verts.Count; i++)
            {
                g.verts[i] = new GeoCoord();
            }
            FaceIJK lastFijk = new FaceIJK();

            for (int vert = 0; vert < Constants.NUM_PENT_VERTS + 1; vert++)
            {
                int v = vert % Constants.NUM_PENT_VERTS;

                FaceIJK fijk = fijkVerts[v];

                int pentLeading4 = 0;
                int overage      = _adjustOverageClassII(ref fijk, adjRes, pentLeading4, 1);
                if (overage == 2) // in a different triangle
                {
                    while (true)
                    {
                        overage = _adjustOverageClassII(ref fijk, adjRes, pentLeading4, 1);
                        if (overage != 2) // not in a different triangle
                        {
                            break;
                        }
                    }
                }

                // all Class III pentagon edges cross icosa edges
                // note that Class II pentagons have vertices on the edge,
                // not edge intersections
                if (H3Index.isResClassIII(res) && vert > 0)
                {
                    // find hex2d of the two vertexes on the last face

                    FaceIJK tmpFijk = new FaceIJK(fijk.face, new CoordIJK(fijk.coord.i, fijk.coord.j, fijk.coord.k));

                    Vec2d orig2d0 = new Vec2d();
                    CoordIJK._ijkToHex2d(lastFijk.coord, ref orig2d0);

                    int currentToLastDir = adjacentFaceDir[tmpFijk.face, lastFijk.face];

                    FaceOrientIJK fijkOrient =
                        new FaceOrientIJK(
                            faceNeighbors[tmpFijk.face, currentToLastDir].face,
                            faceNeighbors[tmpFijk.face, currentToLastDir].translate.i,
                            faceNeighbors[tmpFijk.face, currentToLastDir].translate.j,
                            faceNeighbors[tmpFijk.face, currentToLastDir].translate.k,
                            faceNeighbors[tmpFijk.face, currentToLastDir].ccwRot60
                            );

//                        faceNeighbors[tmpFijk.face,currentToLastDir];

                    tmpFijk.face = fijkOrient.face;
                    //CoordIJK ijk = tmpFijk.coord;
                    CoordIJK ijk = new CoordIJK(tmpFijk.coord.i, tmpFijk.coord.j, tmpFijk.coord.k);

                    // rotate and translate for adjacent face
                    for (int i = 0; i < fijkOrient.ccwRot60; i++)
                    {
                        CoordIJK._ijkRotate60ccw(ref ijk);
                    }

                    CoordIJK transVec = fijkOrient.translate;
                    CoordIJK._ijkScale(ref transVec, unitScaleByCIIres[adjRes] * 3);
                    CoordIJK._ijkAdd(ijk, transVec, ref ijk);
                    CoordIJK._ijkNormalize(ref ijk);

                    Vec2d orig2d1 = new Vec2d();
                    CoordIJK._ijkToHex2d(ijk, ref orig2d1);

                    // find the appropriate icosa face edge vertexes
                    int   maxDim = maxDimByCIIres[adjRes];
                    Vec2d v0     = new Vec2d(3.0 * maxDim, 0.0);
                    Vec2d v1     = new Vec2d(-1.5 * maxDim, 3.0 * Constants.M_SQRT3_2 * maxDim);
                    Vec2d v2     = new Vec2d(-1.5 * maxDim, -3.0 * Constants.M_SQRT3_2 * maxDim);

                    Vec2d edge0 = new Vec2d();
                    Vec2d edge1 = new Vec2d();
                    switch (adjacentFaceDir[tmpFijk.face, fijk.face])
                    {
                    case IJ:
                        edge0 = v0;
                        edge1 = v1;
                        break;

                    case JK:
                        edge0 = v1;
                        edge1 = v2;
                        break;

                    case KI:
                    default:
                        if (adjacentFaceDir[tmpFijk.face, fijk.face] != KI)
                        {
                            throw new Exception("assert(adjacentFaceDir[tmpFijk.face][fijk.face] == KI);");
                        }
                        edge0 = v2;
                        edge1 = v0;
                        break;
                    }

                    // find the intersection and add the lat/lon point to the result
                    Vec2d inter = new Vec2d();
                    Vec2d._v2dIntersect(orig2d0, orig2d1, edge0, edge1, ref inter);
                    var gnv = g.verts[g.numVerts];
                    _hex2dToGeo(ref inter, tmpFijk.face, adjRes, 1, ref gnv);
                    g.verts[g.numVerts] = gnv;
                    g.numVerts++;
                }

                // convert vertex to lat/lon and add to the result
                // vert == NUM_PENT_VERTS is only used to test for possible intersection
                // on last edge
                if (vert < Constants.NUM_PENT_VERTS)
                {
                    Vec2d vec = new Vec2d();
                    CoordIJK._ijkToHex2d(fijk.coord, ref vec);
                    var gnv = g.verts[g.numVerts];
                    _hex2dToGeo(ref vec, fijk.face, adjRes, 1, ref gnv);
                    g.verts[g.numVerts] = gnv;
                    g.numVerts++;
                }
                lastFijk = fijk;
            }
        }