public async Task given_feature_in_projected_coords_process_into_basic_tile()
        {
            Container.GetService <MockContextRepository>().TryGetAs <MockTileContext>("base", out MockTileContext context);
            var accessor = new LayerTileCacheAccessor(() => null, () => new MockRawCacheStorage());

            var generator = new Generator(context, accessor, new LayerInitializationFileService(Container.GetService <IFileProvider>()));
            await generator.GenerateTile(0, 0, 0);

            var tile = accessor.GetRawTile(context.Identifier, 0, 0, 0);

            Assert.AreEqual(0d, tile.X);
            Assert.AreEqual(0d, tile.Y);
            Assert.AreEqual(1d, tile.ZoomSquared);
            Assert.AreEqual(386, tile.NumberOfPoints);
            Assert.AreEqual(6, tile.NumberOfSimplifiedPoints);
            Assert.AreEqual(1, tile.NumberOfFeatures);
        }
        public void given_feature_in_projected_coords_process_into_second_level_tile()
        {
            Container.GetService <MockContextRepository>().TryGetAs <MockTileContext>("base", out MockTileContext context);

            var coloradoFeature = Container.GetService <IConfigurationStrategy>().Into <List <Feature> >("colorado_outline_projected");

            context.TileFeatures = coloradoFeature;

            var accessor  = new LayerTileCacheAccessor(() => null, () => new MockRawCacheStorage());
            var generator = new Generator(context, accessor, new LayerInitializationFileService(Container.GetService <IFileProvider>()));

            generator.SplitTile(context.TileFeatures.ToArray(), zoom: 0, x: 0, y: 0, currentZoom: 1, currentX: 0, currentY: 0);
            generator.SplitTile(context.TileFeatures.ToArray(), zoom: 1, x: 0, y: 0, currentZoom: 2, currentX: 0, currentY: 1);
            generator.SplitTile(context.TileFeatures.ToArray(), zoom: 2, x: 0, y: 1, currentZoom: 3, currentX: 1, currentY: 3);
            var tile = accessor.GetRawTile(context.Identifier, 0, 0, 0);

            Assert.AreEqual(0d, tile.X);
            Assert.AreEqual(0d, tile.Y);
            Assert.AreEqual(1d, tile.ZoomSquared);
            Assert.AreEqual(386, tile.NumberOfPoints);
            Assert.AreEqual(6, tile.NumberOfSimplifiedPoints);
            Assert.AreEqual(1, tile.NumberOfFeatures);
        }
        public async Task <ITile> GenerateTile(int zoomLevel = 0, double x = 0, double y = 0)
        {
            if (tileContext.TileFeatures == null)
            {
                if (tileInitService == null)
                {
                    throw new NotSupportedException("The Features were null and the Generator attempted to initialize a layer with a null LayerInitializer.");
                }

                tileContext.TileFeatures = await tileInitService.InitializeLayer(tileContext.Identifier);

                // This is only called at the beginning //
                SplitTile(tileContext.TileFeatures.ToArray(),
                          zoom: 0, x: 0, y: 0, currentZoom: null, currentX: null, currentY: null);
            }

            // x must be the reduced value from the squared version of the zoom //
            // verify that the caller used this algorithm: //
            var zoomSqr = 1 << zoomLevel;
            var xDenom  = ((x % zoomSqr) + zoomSqr) % zoomSqr; //

            if (xDenom != x)
            {
                throw new NotSupportedException($"The value for X (current value:{x}) was not properly calculated.");
            }

            var tile = cacheAccessor.GetRawTile(tileContext.Identifier, zoomLevel, x, y);

            if (tile != null)
            {
                return(tile);
            }

            var   z0     = zoomLevel;
            var   x0     = x;
            var   y0     = y;
            ITile parent = null;

            while (parent == null && z0 > 0)
            {
                z0--;
                x0 = Math.Floor(x0 / 2);
                y0 = Math.Floor(y0 / 2);

                parent = cacheAccessor.GetRawTile(tileContext.Identifier, z0, x0, y0);
            }

            if (parent == null || parent.Source == null)
            {
                return(null);
            }

            if (transform.IsClippedSquare(parent, tileContext.Extent, tileContext.Buffer))
            {
                return(parent);
            }

            var solid = SplitTile(parent.Source.ToArray(), z0, (int)x0, (int)y0, zoomLevel, (int)x, (int)y);

            ITile currentTile = null;

            if (solid.HasValue)
            {
                var m = 1 << (zoomLevel - solid);
                currentTile = cacheAccessor.GetRawTile(tileContext.Identifier,
                                                       solid.Value, (int)Math.Floor((double)(x / m)), (int)Math.Floor((double)(y / m)));
            }
            else
            {
                currentTile = cacheAccessor.GetRawTile(tileContext.Identifier, zoomLevel, x, y);
            }

            return(currentTile);
        }