Beispiel #1
0
        /// <summary>
        /// Get the voronoi cell for the given point using the clip polygon.
        /// </summary>
        /// <param name="point"></param>
        /// <param name="clipPolygon"></param>
        /// <returns></returns>
        public Cell GetVoronoiCell(Point point, Vector2[] clipPolygon)
        {
            List <Vector2> currentPolygon = GetCircumCenterVectors(point);

            if (currentPolygon.Count == 0)
            {
                return(null);
            }

            Cell cell;

            if (clipPolygon == null)
            {
                Vector2 centroid = PolygonUtils.GetMeanVector(currentPolygon.ToArray());
                cell = new Cell(currentPolygon.ToArray(), centroid, ToVector(point));
            }
            else
            {
                Vector2[] clippedPoints = SutherlandHodgman.GetIntersectedPolygon(currentPolygon.ToArray(), clipPolygon);

                if (clippedPoints.Length == 0)
                {
                    UnityEngine.Debug.Log("Clipping algorithm returned non-intersecting polygon. Skipping it.");
                    return(null);
                }

                Vector2 centroid = PolygonUtils.GetMeanVector(clippedPoints);

                // create the cell including polygons and center point
                cell = new Cell(clippedPoints, centroid, ToVector(point));
            }

            return(cell);
        }
    private void TestSutherlandHodgman(List <MyVector2> poly, List <MyVector2> clipPoly)
    {
        //Normalize to range 0-1
        //We have to use all data to normalize
        List <MyVector2> allPoints = new List <MyVector2>();

        allPoints.AddRange(poly);
        allPoints.AddRange(clipPoly);

        Normalizer2 normalizer = new Normalizer2(allPoints);

        List <MyVector2> poly_normalized = normalizer.Normalize(poly);

        List <MyVector2> clipPoly_normalized = normalizer.Normalize(clipPoly);


        //Main algorithm
        List <MyVector2> polygonAfterClipping_Normalized = SutherlandHodgman.ClipPolygon(poly_normalized, clipPoly_normalized);


        //UnNormalize
        List <MyVector2> polygonAfterClipping = normalizer.UnNormalize(polygonAfterClipping_Normalized);

        //2d to 3d
        List <Vector3> polygonAfterClipping3D = new List <Vector3>();

        foreach (MyVector2 v in polygonAfterClipping)
        {
            polygonAfterClipping3D.Add(v.ToVector3());
        }

        //Display
        DisplayPolygon(polygonAfterClipping3D, Color.red);
    }
        public MainWindow()
        {
            InitializeComponent();
            SetupButtonTab();

            _width      = (int)PaintSurface.Width;
            _height     = (int)PaintSurface.Height;
            _sourceRect = new Int32Rect(0, 0, _width, _height);
            SetupBitmap();
            _sourceBuffer = new byte[_width * _height * (_wb.Format.BitsPerPixel / 8)];
            ClearScreen();
            Instance = this;
            RedrawAll();

            var points1 = new System.Windows.Point[] { new System.Windows.Point(1, 1), new System.Windows.Point(1, 50), new System.Windows.Point(50, 1) };
            var points2 = new System.Windows.Point[] { new System.Windows.Point(10, 10), new System.Windows.Point(10, 50), new System.Windows.Point(50, 10) };
            var clip    = SutherlandHodgman.GetIntersectedPolygon(points1, points2);

            List <Point> pointz = new List <Point>()
            {
                new Point(3, 0),
                new Point(0, 3),
                new Point(3, 6),
                new Point(6, 3)
            };
            var filling = ScanLine.PolygonFilling(pointz, out var colorTab, true);

            UpdateDotProducts();
            StartMainLoop();
        }
Beispiel #4
0
        /// <summary>
        /// Get the voronoi cell for the given point using the clip polygon.
        /// </summary>
        /// <param name="point"></param>
        /// <param name="clipPolygon"></param>
        /// <returns></returns>
        public Cell GetVoronoiCell(Point point, Vector[] clipPolygon)
        {
            List <Vector> currentPolygon = GetCircumCenterVectors(point);

            if (currentPolygon.Count == 0)
            {
                return(null);
            }

            Cell cell;

            if (clipPolygon == null)
            {
                Vector centroid = PolygonUtils.GetMeanVector(currentPolygon.ToArray());
                cell = new Cell(currentPolygon.ToArray(), centroid, ToVector(point));
            }
            else
            {
                Vector[] clippedPoints = SutherlandHodgman.GetIntersectedPolygon(currentPolygon.ToArray(), clipPolygon);

                Vector centroid = PolygonUtils.GetMeanVector(clippedPoints);

                // create the cell including polygons and center point
                cell = new Cell(clippedPoints, centroid, ToVector(point));
            }

            return(cell);
        }
Beispiel #5
0
        private static IList <Coordinate> GetIntersectedPolygon(Point[] points, Point[] clipPoly)
        {
            List <Coordinate> p = new List <Coordinate>();

            foreach (var point in points)
            {
                p.Add(new Coordinate(point.X, point.Y));
            }
            return(SutherlandHodgman.GetIntersectedPolygon(p, clipPoly));
        }
Beispiel #6
0
        /// <summary>
        /// Create Biome masks for the specified bounds
        /// </summary>
        /// <param name="bounds"></param>
        private void CreateMasks(Bounds bounds)
        {
            float outerRadius = GetOuterRadius(bounds);

            // bounds for clipping
            Vector2[] clipPolygon = editor.GetBiomeClipPolygon(bounds);

            float density = editor.extension.biomeSettings.density;

            List <Vector3> positions = GetPositions(bounds);

            foreach (Vector3 position in positions)
            {
                // skip randomly
                if (density != 1 && UnityEngine.Random.Range(0f, 1f) >= density)
                {
                    continue;
                }

                Vector3[] hexagon = ShapeCreator.CreateHexagon(position, outerRadius);

                // clip, convert to vector2
                Vector2[] polygonXY     = hexagon.Select(item => new Vector2(item.x, item.z)).ToArray();
                Vector2[] clippedPoints = SutherlandHodgman.GetIntersectedPolygon(polygonXY, clipPolygon);

                if (clippedPoints == null || clippedPoints.Length < 3)
                {
                    continue;
                }

                // convert back to vector3
                hexagon = clippedPoints.Select(item => new Vector3(item.x, 0, item.y)).ToArray();

                int maskId = editor.GetNextMaskId();

                List <Vector3> nodes = hexagon.OfType <Vector3>().ToList();

                // apply random shape if requested
                if (editor.extension.shapeSettings.randomShape)
                {
                    nodes = ShapeCreator.CreateRandomShape(nodes,                                             //
                                                           editor.extension.shapeSettings.RandomConvexity,    //
                                                           editor.extension.shapeSettings.keepOriginalPoints, //
                                                           editor.extension.shapeSettings.RandomPointsCount,  //
                                                           editor.extension.shapeSettings.randomAngle,        //
                                                           editor.extension.shapeSettings.douglasPeuckerReductionTolerance);
                }

                CreateBiomeMaskArea("Biome Mask " + maskId, "Mask " + maskId, position, nodes);
            }
        }
        private void CreateBiomeMaskArea(string gameObjectName, string maskName, Bounds bounds)
        {
            // modify bounds, reduce its rectangular distribution
            float minFactor = editor.extension.rectangularPartitionSettings.boundsShiftFactorMin;
            float maxFactor = editor.extension.rectangularPartitionSettings.boundsShiftFactorMax;

            bounds = PolygonUtils.ShiftResizeBounds(bounds, minFactor, maxFactor);

            List <Vector3> nodes;

            if (editor.extension.shapeSettings.randomShape)
            {
                // old mechanism: convex hull
                // nodes = ShapeCreator.CreateRandomShapeUsingConvexHull(bounds, randomPointCount);

                // new mechanism: random shape with parameters like convexity
                nodes = ShapeCreator.CreateRandomShape(
                    ShapeCreator.CreateRectangularBoundsShape(bounds),
                    editor.extension.shapeSettings.RandomConvexity,     //
                    editor.extension.shapeSettings.keepOriginalPoints,  //
                    editor.extension.shapeSettings.RandomPointsCount,   //
                    editor.extension.shapeSettings.randomAngle,         //
                    editor.extension.shapeSettings.douglasPeuckerReductionTolerance);
            }
            // exact bounds
            else
            {
                nodes = ShapeCreator.CreateRectangularBoundsShape(bounds);
            }


            // bounds for clipping
            Vector2[] clipPolygon = editor.GetBiomeClipPolygon(bounds);

            // clip, convert to vector2
            Vector2[] polygonXY     = nodes.Select(item => new Vector2(item.x, item.z)).ToArray();
            Vector2[] clippedPoints = SutherlandHodgman.GetIntersectedPolygon(polygonXY, clipPolygon);

            if (clippedPoints == null || clippedPoints.Length < 3)
            {
                return;
            }

            // convert back to vector3
            nodes = clippedPoints.Select(item => new Vector3(item.x, 0, item.y)).ToList();

            // create the biome mask using the nodes
            CreateBiomeMaskArea(gameObjectName, maskName, bounds, nodes);
        }
Beispiel #8
0
        /// <summary>
        /// Get a list of all polygons per point.
        /// This contains duplicate edges if multiple points share the same edge.
        /// The polygons are clipped at the canvas bounds.
        /// Basically this is the method to use. It gives you all Voronoi polygons exactly on the Canvas
        /// </summary>
        /// <returns></returns>
        private List <Vector[]> GetAllClippedVoronoiPolygons()
        {
            // bounding box
            Vector[] clipPolygon = GetClipPolygon(clipAtBoundsMargin);

            List <Vector[]> allVoronoiPolygons = new List <Vector[]>();

            foreach (Vector[]  polygon in graph.GetAllVoronoiPolygons())
            {
                Vector[] intersectedPolygon = SutherlandHodgman.GetIntersectedPolygon(polygon, clipPolygon);
                allVoronoiPolygons.Add(intersectedPolygon);
            }

            return(allVoronoiPolygons);
        }
Beispiel #9
0
        /// <summary>
        /// Clip the specified polygon at the canvas bounds. Consider a margin.
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        private Vector[] ClipAtBounds(Vector[] currentPolygon)
        {
            // ensure there are data
            if (currentPolygon.Length == 0)
            {
                return(currentPolygon);
            }

            // clip polygon, bounding box
            Vector[] clipPolygon = GetClipPolygon(clipAtBoundsMargin);

            Vector[] intersectedPolygon = SutherlandHodgman.GetIntersectedPolygon(currentPolygon, clipPolygon);

            return(intersectedPolygon);
        }
        public void ClipRectangle()
        {
            ReadOnlySpan <Point> subject = stackalloc Point[4] {
                new Point(0d, 0d), new Point(2d, 0d), new Point(2d, 2d), new Point(0d, 2d)
            };
            ReadOnlySpan <Point> clipping = stackalloc Point[4] {
                new Point(1d, 0d), new Point(3d, 0d), new Point(3d, 2d), new Point(1d, 2d)
            };
            ReadOnlySpan <Point> result = stackalloc Point[4] {
                new Point(1d, 2d), new Point(2d, 2d), new Point(2d, 0d), new Point(1d, 0d)
            };

            var intersect = new SutherlandHodgman().GetIntersectedPolygon(subject, clipping).Span;

            Assert.Equal(result.Length, intersect.Length);
            for (var i = 0; i < intersect.Length; i++)
            {
                Assert.Equal(result[i], intersect[i]);
            }
        }
        private void IntersectAllPolygonsWithAnimationPolygon()
        {
            Polygons.RemoveAll(x => x.Points.Count < 3);

            for (int i = 0; i < Polygons.Count; i++)
            {
                if (Polygons[i] == animationPolygon)
                {
                    continue;
                }

                var intersectedPoints = SutherlandHodgman.GetIntersectedPolygon(Polygons[i].Points.ToArray(), animationPolygon.Points.ToArray());

                Polygon intersectedPolygn = new Polygon(_drawing);
                intersectedPolygn.AddVerticle(intersectedPoints);
                intersectedPolygn.FillingColor = Polygons[i].FillingColor;
                intersectedPolygn.FillEnabled  = true;
                intersectedPolygn.DrawPolygon(true);
            }
        }
Beispiel #12
0
        private void CompositionTargetOnRendering(object sender, EventArgs e)
        {
            if (!_running)
            {
                return;
            }
            foreach (var colorPolygon in _generated)
            {
                colorPolygon.Clear();
                colorPolygon.Vertices = colorPolygon.Vertices.Select(v => new Vertex(0, new Point(v.Point.X + _speed, v.Point.Y))).ToList();
//                colorPolygon.ShiftInterior(_speed);
                colorPolygon.Shift.X += _speed;
            }

            if (_clearCounter++ % 60 == 0)
            {
                ClearGenerated();
            }

            ClearClippedOnScreen();
            _clipped.Clear();
            foreach (var subject in _polygons)
            {
                foreach (var clip in _generated)
                {
                    var temppoly = SutherlandHodgman.GetIntersectedPolygon(
                        subject.Vertices.Select(p => new System.Windows.Point(p.Point.X, p.Point.Y)).ToArray(),
                        clip.Vertices.Select(p => new System.Windows.Point(p.Point.X, p.Point.Y)).ToArray());

                    if (temppoly.Length != 0)
                    {
                        var poly    = temppoly.Select(p => new Point((int)p.X, (int)p.Y)).ToList();
                        var filling = ScanLine.PolygonFilling(poly, out Color[,] xd);
                        var polygon = new ClippedPolygon(filling, clip);
                        _clipped.Add(polygon);
                    }
                }
            }

            RedrawAll();
        }
        public ActionResult Post([FromBody] PolygonClipRequestPayload request)
        {
            if (string.IsNullOrEmpty(request.SourcePolygon))
            {
                return(BadRequest("Missing source polygon"));
            }

            if (string.IsNullOrEmpty(request.ClipPolygon))
            {
                return(BadRequest("Missing clipping polygon"));
            }

            var source = PathMarkupConverter.Convert(request.SourcePolygon);
            var clip   = PathMarkupConverter.Convert(request.ClipPolygon);

            var result = new SutherlandHodgman().GetIntersectedPolygon(source, clip);

            return(Ok(new PolygonResultPayload
            {
                ResultPolygon = PathMarkupConverter.Convert(result)
            }));
        }
    private void TestSutherlandHodgman(List <MyVector2> poly, List <MyVector2> clipPoly)
    {
        //Normalize to range 0-1
        //We have to use all data to normalize
        List <MyVector2> allPoints = new List <MyVector2>();

        allPoints.AddRange(poly);
        allPoints.AddRange(clipPoly);

        AABB2 normalizingBox = new AABB2(allPoints);

        float dMax = HelpMethods.CalculateDMax(normalizingBox);

        List <MyVector2> poly_normalized = HelpMethods.Normalize(poly, normalizingBox, dMax);

        List <MyVector2> clipPoly_normalized = HelpMethods.Normalize(clipPoly, normalizingBox, dMax);


        //Main algorithm
        List <MyVector2> polygonAfterClipping_Normalized = SutherlandHodgman.ClipPolygon(poly_normalized, clipPoly_normalized);


        //UnNormalize
        List <MyVector2> polygonAfterClipping = HelpMethods.UnNormalize(polygonAfterClipping_Normalized, normalizingBox, dMax);

        //2d to 3d
        List <Vector3> polygonAfterClipping3D = new List <Vector3>();

        foreach (MyVector2 v in polygonAfterClipping)
        {
            polygonAfterClipping3D.Add(v.ToVector3());
        }

        //Display
        DisplayPolygon(polygonAfterClipping3D, Color.red);
    }
Beispiel #15
0
            ////////////////////////////////////////////////////////////////////////
            //--------------------------------- REVISIONS --------------------------
            // Date       Name                 Tracking #         Description
            // ---------  -------------------  -------------      ------------------
            // 18JUN2009  James Shen                              Initial Creation
            ////////////////////////////////////////////////////////////////////////

            /**
             *
             * @param mapDirection
             * @param X
             * @param Y
             * @param Width
             * @param Height
             */
            private void DrawRouteImage(MapDirection mapDirection, int x, int y,
                                        int width, int height)
            {
                if (!_routeDrawWaypointOnly)
                {
                    _sutherlandHodgman = new SutherlandHodgman(_screenBounds);

                    ArrayList polyline = new ArrayList();
                    int       minLevel = NeedShowLevel(mapDirection.Polyline.NumLevels, GetZoom());
                    for (int i = 0; i < mapDirection.Polyline.GetVertexCount(); i++)
                    {
                        int level = mapDirection.Polyline.GetLevel(i);
                        if (level >= minLevel)
                        {
                            polyline.Add(mapDirection.Polyline.GetVertex(i));
                        }
                    }
                    ArrayList clippedPts = _sutherlandHodgman.ClipPline(polyline);

                    GeoPoint newPt1;
                    GeoPoint newPt2;

                    GeoPoint  drawPt1 = new GeoPoint(0, 0), drawPt2 = new GeoPoint(0, 0);
                    const int steps         = 1;
                    int       numOfTiles    = MAP_TILE_WIDTH / _mapDrawingTileWidth;
                    Rectangle drawArea      = new Rectangle();
                    Rectangle intersectRect = new Rectangle(0, 0, width, height);
                    int       xIndex;
                    for (xIndex = 0; xIndex < numOfTiles; xIndex++)
                    {
                        int yIndex;
                        for (yIndex = 0; yIndex < numOfTiles; yIndex++)
                        {
                            bool      hasPt1 = false;
                            GeoLatLng pt1    = null;
                            _routeGraphics2D.Clear(Color.White);
                            drawArea.X     = xIndex * _mapDrawingTileWidth;
                            drawArea.Y     = yIndex * _mapDrawingTileWidth;
                            drawArea.Width = drawArea.Height = _mapDrawingTileWidth;
                            drawArea       = intersectRect.Intersection(drawArea);
                            int totalPointSize = clippedPts.Count;
                            if (!drawArea.IsEmpty())
                            {
                                _routeGraphics2D.SetClip(0, 0,
                                                         drawArea.Width, drawArea.Height);
                                try
                                {
                                    for (int j = 0; j < totalPointSize; j += steps)
                                    {
                                        GeoLatLng pt    = (GeoLatLng)clippedPts[j];
                                        int       level = minLevel;
                                        if (hasPt1 == false)
                                        {
                                            if (level >= minLevel)
                                            {
                                                {
                                                    {
                                                        hasPt1 = true;
                                                        pt1    = pt;
                                                        continue;
                                                    }
                                                }
                                            }
                                        }
                                        if (hasPt1)
                                        {
                                            if (level >= minLevel)
                                            {
                                                GeoLatLng pt2 = pt;
                                                newPt1    = FromLatLngToMapPixel(pt1);
                                                newPt2    = FromLatLngToMapPixel(pt2);
                                                newPt1.X -= x + xIndex * _mapDrawingTileWidth;
                                                newPt1.Y -= y + yIndex * _mapDrawingTileWidth;
                                                newPt2.X -= x + xIndex * _mapDrawingTileWidth;
                                                newPt2.Y -= y + yIndex * _mapDrawingTileWidth;
                                                drawPt1.X = (int)newPt1.X;
                                                drawPt1.Y = (int)newPt1.Y;
                                                drawPt2.X = (int)newPt2.X;
                                                drawPt2.Y = (int)newPt2.Y;

                                                if ((drawPt1.Distance(drawPt2) > 0))
                                                {
                                                    _routeGraphics2D.DrawLine(RoutePen, (int)drawPt1.X,
                                                                              (int)drawPt1.Y,
                                                                              (int)drawPt2.X, (int)drawPt2.Y);
                                                    pt1 = pt2;
                                                    if (_readListener != null)
                                                    {
                                                        _readListener.readProgress(j,
                                                                                   totalPointSize);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                catch (Exception)
                                {
                                }
                            }

                            _routeGraphics.DrawRGB(_routeGraphics2D.GetRGB(), 0,
                                                   _mapDrawingTileWidth,
                                                   xIndex * _mapDrawingTileWidth,
                                                   yIndex * _mapDrawingTileWidth,
                                                   _mapDrawingTileWidth,
                                                   _mapDrawingTileWidth, true);
                        }
                    }
                }
                else
                {
                    _routeGraphics.SetColor(TRANSPARENCY);
                    _routeGraphics.FillRect(0, 0, MAP_TILE_WIDTH, MAP_TILE_WIDTH);
                }
            }
Beispiel #16
0
        /// <summary>
        /// 切割矢量并输出成bitmap
        /// </summary>
        /// <param name="featureCollection"></param>
        /// <param name="outputDir"></param>
        public void Output(FeatureCollection featureCollection, string outputDir)
        {
            var _tileDictionary = _vectorPyramid.TileDictionary;
            int _tileSize       = _vectorPyramid.Projection.TileSize;

            //
            foreach (int zoom in _tileDictionary.Keys)
            {
                var tileCollection = _tileDictionary[zoom];
                foreach (var tile in tileCollection)
                {
                    try
                    {
                        //
                        Bitmap   bmp = new Bitmap(_tileSize, _tileSize);
                        Graphics g   = Graphics.FromImage(bmp);
                        Pen      pen = new Pen(Color.Black, 3);
                        //
                        for (int i = 0; i < featureCollection.Count; i++)
                        {
                            IFeature f = featureCollection[i];
                            //点
                            if (f.Geometry.OgcGeometryType == OgcGeometryType.Point)
                            {
                                Coordinate point = f.Geometry.Coordinate;
                                if (tile.Bound.PointInPolygon(point))
                                {
                                    //2.2.1 计算点的像素坐标
                                    Coordinate pixel = _vectorPyramid.Projection.LatlngToPoint(point, zoom);
                                    //
                                    double deltaX = pixel.X / _tileSize - tile.X;
                                    double deltaY = pixel.Y / _tileSize - tile.Y;
                                    int    x      = Convert.ToInt32(deltaX * _tileSize);
                                    int    y      = Convert.ToInt32(deltaY * _tileSize);
                                    g.DrawLine(pen, x, x, x, y);
                                }
                                continue;
                            }
                            //线
                            else if (f.Geometry.OgcGeometryType == OgcGeometryType.LineString)
                            {
                                //2.1瓦片裁剪道路
                                List <Coordinate> clipLine = CohenSutherland.GetIntersectedPolyline(f.Geometry.Coordinates, tile.Bound);
                                if (clipLine.Count == 0)
                                {
                                    continue;
                                }
                                int x0 = -1000, y0 = -1000;
                                //2.2 绘制clipLine
                                foreach (Coordinate point in clipLine)
                                {
                                    //2.2.1 计算点的像素坐标
                                    Coordinate pixel = _vectorPyramid.Projection.LatlngToPoint(point, zoom);
                                    //
                                    double deltaX = pixel.X / _tileSize - tile.X;
                                    double deltaY = pixel.Y / _tileSize - tile.Y;
                                    int    x      = Convert.ToInt32(deltaX * _tileSize);
                                    int    y      = Convert.ToInt32(deltaY * _tileSize);
                                    if (x0 == -1000 && y0 == -1000)
                                    {
                                        x0 = x;
                                        y0 = y;
                                        continue;
                                    }
                                    else
                                    {
                                        g.DrawLine(pen, x0, y0, x, y);
                                        x0 = x;
                                        y0 = y;
                                    }
                                }
                            }
                            //面
                            else if (f.Geometry.OgcGeometryType == OgcGeometryType.Polygon)
                            {
                                List <Coordinate> clipPolygon = SutherlandHodgman.GetIntersectedPolygon(f.Geometry.Coordinates, tile.Bound);
                                if (clipPolygon.Count < 3)
                                {
                                    continue;
                                }
                                int x0 = -1000, y0 = -1000;
                                //2.2 绘制clipLine
                                foreach (Coordinate point in clipPolygon)
                                {
                                    //2.2.1 计算点的像素坐标
                                    Coordinate pixel = _vectorPyramid.Projection.LatlngToPoint(point, zoom);
                                    //
                                    double deltaX = pixel.X / _tileSize - tile.X;
                                    double deltaY = pixel.Y / _tileSize - tile.Y;
                                    int    x      = Convert.ToInt32(deltaX * _tileSize);
                                    int    y      = Convert.ToInt32(deltaY * _tileSize);
                                    if (x0 == -1000 && y0 == -1000)
                                    {
                                        x0 = x;
                                        y0 = y;
                                        continue;
                                    }
                                    else
                                    {
                                        g.DrawLine(pen, x0, y0, x, y);
                                        x0 = x;
                                        y0 = y;
                                    }
                                }
                            }
                        }
                        //2.3 保存bmp到指定路径
                        if (!System.IO.Directory.Exists(outputDir + @"\" + zoom))
                        {
                            System.IO.Directory.CreateDirectory(outputDir + @"\" + zoom);
                        }
                        //根据geometry id存储,获取不到geometry的id,所以只能自定内部序号
                        bmp.Save(outputDir + @"\" + zoom + @"\" + tile.X + "_" + tile.Y + "_" + tile.Z + ".jpg");
                    }
                    catch
                    {
                        continue;
                    }
                }
            }
        }