public int?SplitTile(List <VectorTileFeature> startfeatures, VectorTileCoord startCoord, int?cz = null, int?cx = null, int?cy = null) { var stack = new Stack <GeoJsonVTStackItem>(); stack.Push(new GeoJsonVTStackItem { Features = startfeatures, Coord = startCoord }); int?solid = null; while (stack.Count > 0) { var item = stack.Pop(); var features = item.Features; var x = item.Coord.X; var y = item.Coord.Y; var z = item.Coord.Z; var z2 = 1 << z; var id = item.Coord.ToID(); VectorTile tile = Tiles.Contains(id) ? Tiles.Get(id) : null; if (tile == null) { var tileTolerance = z == Options.MaxZoom ? 0 : Options.Tolerance / (z2 * Options.Extent); tile = Tiles.Set(id, VectorTile.CreateTile(features, z2, x, y, tileTolerance, z == Options.MaxZoom)); Tiles.TileCoords.Add(new VectorTileCoord(z, x, y)); } // save reference to original geometry in tile so that we can drill down later if we stop now tile.Source = features; // if it's the first-pass tiling if (!cz.HasValue) { // stop tiling if we reached max zoom, or if the tile is too simple if (z == Options.IndexMaxZoom || tile.NumPoints <= Options.IndexMaxPoints) { continue; } // if a drilldown to a specific tile } else { // stop tiling if we reached base zoom or our target tile zoom if (z == Options.MaxZoom) { continue; } // stop tiling if it's not an ancestor of the target tile if (cz.HasValue) { if (z == cz.Value) { continue; } var m = 1 << (cz.Value - z); if (x != (int)Math.Floor((double)cx.Value / m) || y != (int)Math.Floor((double)cy.Value / m)) { continue; } } } // stop tiling if the tile is solid clipped square if (!Options.SolidChildren && IsClippedSquare(tile, Options.Extent, Options.Buffer)) { if (cz.HasValue) { solid = z; // and remember the zoom if we're drilling down } continue; } // if we slice further down, no need to keep source geometry tile.Source = null; // if (debug > 1) console.time('clipping'); // values we'll use for clipping var k1 = 0.5 * Options.Buffer / Options.Extent; var k2 = 0.5 - k1; var k3 = 0.5 + k1; var k4 = 1 + k1; List <VectorTileFeature> tl, bl, tr, br, left, right; tl = bl = tr = br = null; left = Clipper.Clip(features, z2, x - k1, x + k3, 0, IntersectX, tile.Min[0], tile.Max[0]); right = Clipper.Clip(features, z2, x + k2, x + k4, 0, IntersectX, tile.Min[0], tile.Max[0]); if (left.HasAny()) { tl = Clipper.Clip(left, z2, y - k1, y + k3, 1, intersectY, tile.Min[1], tile.Max[1]); bl = Clipper.Clip(left, z2, y + k2, y + k4, 1, intersectY, tile.Min[1], tile.Max[1]); } if (right.HasAny()) { tr = Clipper.Clip(right, z2, y - k1, y + k3, 1, intersectY, tile.Min[1], tile.Max[1]); br = Clipper.Clip(right, z2, y + k2, y + k4, 1, intersectY, tile.Min[1], tile.Max[1]); } // if (debug > 1) console.timeEnd('clipping'); if (tl.HasAny()) { stack.Push(new GeoJsonVTStackItem { Features = tl, Coord = new VectorTileCoord(z + 1, x * 2, y * 2) }); } if (bl.HasAny()) { stack.Push(new GeoJsonVTStackItem { Features = bl, Coord = new VectorTileCoord(z + 1, x * 2, y * 2 + 1) }); } if (tr.HasAny()) { stack.Push(new GeoJsonVTStackItem { Features = tr, Coord = new VectorTileCoord(z + 1, x * 2 + 1, y * 2) }); } if (br.HasAny()) { stack.Push(new GeoJsonVTStackItem { Features = br, Coord = new VectorTileCoord(z + 1, x * 2 + 1, y * 2 + 1) }); } } return(solid); }