Exemple #1
0
        public void V2DIntersect()
        {
            var p0 = new Vec2d(2.0m, 2.0m);
            var p1 = new Vec2d(6.0m, 6.0m);
            var p2 = new Vec2d(0.0m, 4.0m);
            var p3 = new Vec2d(10.0m, 4.0m);

            var intersection = Vec2d.FindIntersection(p0, p1, p2, p3);

            const decimal expectedX = 4.0m;
            const decimal expectedY = 4.0m;

            Assert.IsTrue(Math.Abs(intersection.X - expectedX) < Constants.H3.DBL_EPSILON);
            Assert.IsTrue(Math.Abs(intersection.Y - expectedY) < Constants.H3.DBL_EPSILON);
        }
        /// <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="start">The first topological vertex to return.</param>
        /// <param name="length">The number of topological vertexes to return.</param>
        /// <returns>The spherical coordinates of the cell boundary.</returns>
        /// <!--
        /// faceijk.c
        /// void _faceIjkPentToGeoBoundary
        /// -->
        public static GeoBoundary PentToGeoBoundary(this FaceIjk h, int res, int start, int length)
        {
            var gb        = new GeoBoundary();
            int adjRes    = res;
            var centerIjk = h;

            IList <FaceIjk> fijkVerts = new FaceIjk[Constants.H3.NUM_PENT_VERTS];

            (_, adjRes, fijkVerts) = centerIjk.PentToVerts(adjRes, fijkVerts);

            // If we're returning the entire loop, we need one more iteration in case
            // of a distortion vertex on the last edge
            int additionalIteration = length == Constants.H3.NUM_PENT_VERTS
                                          ? 1
                                          : 0;

            // convert each vertex to lat/lon
            // adjust the face of each vertex as appropriate and introduce
            // edge-crossing vertices as needed
            gb.NumVerts = 0;
            var lastFijk = new FaceIjk();

            for (int vert = start; vert < start + length + additionalIteration; vert++)
            {
                int v    = vert % Constants.H3.NUM_PENT_VERTS;
                var fijk = fijkVerts[v];
                (_, fijk) = fijk.AdjustPentOverage(adjRes);

                // all Class III pentagon edges cross icosahedron edges
                // note that Class II pentagons have vertices on the edge,
                // not edge intersections
                if (res.IsResClassIii() && vert > start)
                {
                    // find hex2d of the two vertexes on the last face
                    var tmpFijk = fijk;
                    var orig2d0 = lastFijk.Coord.ToHex2d();

                    int currentToLastDir = Constants.FaceIjk.AdjacentFaceDir[tmpFijk.Face, lastFijk.Face];

                    var fijkOrient = Constants.FaceIjk.FaceNeighbors[tmpFijk.Face, currentToLastDir];

                    tmpFijk = tmpFijk.ReplaceFace(fijkOrient.Face);
                    //  Borrow ijk
                    var ijk = tmpFijk.Coord;

                    // rotate and translate for adjacent face
                    for (var i = 0; i < fijkOrient.Ccw60Rotations; i++)
                    {
                        ijk = ijk.Rotate60CounterClockwise();
                    }

                    var transVec = fijkOrient.Translate;
                    var scaleRes = Constants.FaceIjk.UnitScaleByCiiRes[adjRes] * 3;
                    transVec *= scaleRes;
                    ijk      += transVec;
                    ijk       = ijk.Normalized();

                    var orig2d1 = ijk.ToHex2d();

                    // find the appropriate icosahedron face edge vertexes
                    int maxDim = Constants.FaceIjk.MaxDimByCiiRes[adjRes];
                    var v0     = new Vec2d(3.0m * maxDim, 0.0m);
                    var v1     = new Vec2d(-1.5m * maxDim, 3.0m * Constants.H3.M_SQRT3_2 * maxDim);
                    var v2     = new Vec2d(-1.5m * maxDim, -3.0m * Constants.H3.M_SQRT3_2 * maxDim);

                    Vec2d edge0;
                    Vec2d edge1;

                    switch (Constants.FaceIjk.AdjacentFaceDir[tmpFijk.Face, fijk.Face])
                    {
                    case Constants.FaceIjk.IJ:
                        edge0 = v0;    // new Vec2d(v0.X, v0.Y);
                        edge1 = v1;    //new Vec2d(v1.X, v1.Y);
                        break;

                    case Constants.FaceIjk.JK:
                        edge0 = v1;    //new Vec2d(v1.X, v1.Y);
                        edge1 = v2;    //new Vec2d(v2.X, v2.Y);
                        break;

                    default:
                        if (Constants.FaceIjk.AdjacentFaceDir[tmpFijk.Face, fijk.Face] != Constants.FaceIjk.KI)
                        {
                            throw new Exception("assert(adjacentFaceDir[tmpFijk.face][fijk.face] == KI);");
                        }

                        edge0 = v2;    //new Vec2d(v2.X, v2.Y);
                        edge1 = v0;    //new Vec2d(v0.X, v0.Y);
                        break;
                    }

                    // find the intersection and add the lat/lon point to the result
                    var inter = Vec2d.FindIntersection(orig2d0, orig2d1, edge0, edge1);
                    gb.Verts[gb.NumVerts] = inter.ToGeoCoord(tmpFijk.Face, adjRes, 1);
                    gb.NumVerts++;
                }

                // convert vertex to lat/lon and add to the result
                // vert == start + NUM_PENT_VERTS is only used to test for possible
                // intersection on last edge
                if (vert < start + Constants.H3.NUM_PENT_VERTS)
                {
                    gb.Verts[gb.NumVerts] = fijk.Coord
                                            .ToHex2d()
                                            .ToGeoCoord(fijk.Face, adjRes, 1);
                    gb.NumVerts++;
                }

                lastFijk = new FaceIjk(fijk);
            }

            return(gb);
        }
        /// <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="start">The first topological vertex to return</param>
        /// <param name="length">The number of topological vertexes to return</param>
        /// <returns>The spherical coordinates of the cell boundary</returns>
        /// <!--
        /// faceijk.c
        /// void _faceIjkToGeoBoundary
        /// -->
        public static GeoBoundary ToGeoBoundary(this FaceIjk h, int res, int start, int length)
        {
            int             adjRes    = res;
            var             centerIjk = h;
            IList <FaceIjk> fijkVerts = new FaceIjk[Constants.H3.NUM_HEX_VERTS];

            (centerIjk, adjRes, fijkVerts) = centerIjk.ToVerts(adjRes, fijkVerts);

            // If we're returning the entire loop, we need one more iteration in case
            // of a distortion vertex on the last edge
            int additionalIteration = length == Constants.H3.NUM_HEX_VERTS
                                          ? 1
                                          : 0;

            var g = new GeoBoundary {
                NumVerts = 0
            };
            // convert each vertex to lat/lon
            // adjust the face of each vertex as appropriate and introduce
            // edge-crossing vertices as needed
            int lastFace    = -1;
            var lastOverage = Overage.NO_OVERAGE;

            for (int vert = start; vert < start + length + additionalIteration; vert++)
            {
                int       v            = vert % Constants.H3.NUM_HEX_VERTS;
                var       fijk         = fijkVerts[v];
                const int pentLeading4 = 0;

                Overage overage;
                (overage, fijk) = fijk.AdjustOverageClassIi(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 (res.IsResClassIii() && vert > start && fijk.Face != lastFace && lastOverage != Overage.FACE_EDGE)
                {
                    // find hex2d of the two vertexes on original face
                    int lastV   = (v + 5) % Constants.H3.NUM_HEX_VERTS;
                    var orig2d0 = fijkVerts[lastV].Coord.ToHex2d();
                    var orig2d1 = fijkVerts[v].Coord.ToHex2d();

                    // find the appropriate icosahedron face edge vertexes
                    int maxDim = Constants.FaceIjk.MaxDimByCiiRes[adjRes];
                    var v0     = new Vec2d(3.0m * maxDim, 0.0m);
                    var v1     = new Vec2d(-1.5m * maxDim, 3.0m * Constants.H3.M_SQRT3_2 * maxDim);
                    var v2     = new Vec2d(-1.5m * maxDim, -3.0m * Constants.H3.M_SQRT3_2 * maxDim);

                    int face2 = lastFace == centerIjk.Face
                                    ? fijk.Face
                                    : lastFace;
                    Vec2d edge0;
                    Vec2d edge1;
                    switch (Constants.FaceIjk.AdjacentFaceDir[centerIjk.Face, face2])
                    {
                    case Constants.FaceIjk.IJ:
                        edge0 = v0;
                        edge1 = v1;
                        break;

                    case Constants.FaceIjk.JK:
                        edge0 = v1;
                        edge1 = v2;
                        break;

                    case Constants.FaceIjk.KI:
                        edge0 = v2;
                        edge1 = v0;
                        break;

                    default:
                        throw new Exception
                              (
                                  $"(adjacentFaceDir[centerIJK.face][face2] == KI) idx0: {centerIjk.Face} idx1: {face2}"
                              );
                    }

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

                    /*
                     * 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 = orig2d0 == inter || orig2d1 == inter;
                    if (!isIntersectionAtVertex)
                    {
                        g.Verts[g.NumVerts] = inter.ToGeoCoord(centerIjk.Face, adjRes, 1);
                        g.NumVerts++;
                    }
                }

                // convert vertex to lat/lon and add to the result
                // vert == start + NUM_HEX_VERTS is only used to test for possible
                // intersection on last edge
                if (vert < start + Constants.H3.NUM_HEX_VERTS)
                {
                    var vec = fijk.Coord.ToHex2d();
                    g.Verts[g.NumVerts] = vec.ToGeoCoord(fijk.Face, adjRes, 1);
                    g.NumVerts++;
                }

                lastFace    = fijk.Face;
                lastOverage = overage;
            }

            return(g);
        }