/// <summary>
 ///     Initialize VectorTileFeature
 /// </summary>
 /// <param name="layer">Parent <see cref="VectorTileLayer" /></param>
 public VectorTileFeature(VectorTileLayer layer, uint?clipBuffer = null, float scale = 1.0f)
 {
     Layer       = layer;
     _clipBuffer = clipBuffer;
     _scale      = scale;
     Tags        = new List <int>();
 }
        /// <summary>
        ///     Get a feature of the <see cref="VectorTileLayer" />
        /// </summary>
        /// <param name="layer"><see cref="VectorTileLayer" /> containing the feature</param>
        /// <param name="data">Raw byte data of the feature</param>
        /// <param name="validate">If true, run checks if the tile contains valid data. Decreases decoding speed.</param>
        /// <param name="clippBuffer">
        ///     <para>'null': returns the geometries unaltered as they are in the vector tile. </para>
        ///     <para>Any value >=0 clips a border with the size around the tile. </para>
        ///     <para>These are not pixels but the same units as the 'extent' of the layer. </para>
        /// </param>
        /// <returns></returns>
        public static VectorTileFeature GetFeature(
            VectorTileLayer layer
            , byte[] data
            , bool validate   = true
            , uint?clipBuffer = null
            , float scale     = 1.0f
            )
        {
            PbfReader         featureReader = new PbfReader(data);
            VectorTileFeature feat          = new VectorTileFeature(layer, clipBuffer, scale);
            bool geomTypeSet = false;

            while (featureReader.NextByte())
            {
                int featureType = featureReader.Tag;
                if (validate)
                {
                    if (!ConstantsAsDictionary.FeatureType.ContainsKey(featureType))
                    {
                        throw new Exception(string.Format("Layer [{0}] has unknown feature type: {1}", layer.Name,
                                                          featureType));
                    }
                }

                switch ((FeatureType)featureType)
                {
                case FeatureType.Id:
                    feat.Id = (ulong)featureReader.Varint();
                    break;

                case FeatureType.Tags:
                    List <int> tags = featureReader.GetPackedUnit32().Select(t => (int)t).ToList();
                    feat.Tags = tags;
                    break;

                case FeatureType.Type:
                    int geomType = (int)featureReader.Varint();
                    if (validate)
                    {
                        if (!ConstantsAsDictionary.GeomType.ContainsKey(geomType))
                        {
                            throw new Exception(string.Format("Layer [{0}] has unknown geometry type tag: {1}",
                                                              layer.Name, geomType));
                        }
                    }

                    feat.GeometryType = (GeomType)geomType;
                    geomTypeSet       = true;
                    break;

                case FeatureType.Geometry:
                    if (null != feat.GeometryCommands)
                    {
                        throw new Exception(
                                  string.Format("Layer [{0}], feature already has a geometry", layer.Name));
                    }

                    //get raw array of commands and coordinates
                    feat.GeometryCommands = featureReader.GetPackedUnit32();
                    break;

                default:
                    featureReader.Skip();
                    break;
                }
            }

            if (validate)
            {
                if (!geomTypeSet)
                {
                    throw new Exception(string.Format("Layer [{0}]: feature missing geometry type", layer.Name));
                }

                if (null == feat.GeometryCommands)
                {
                    throw new Exception(string.Format("Layer [{0}]: feature has no geometry", layer.Name));
                }

                if (0 != feat.Tags.Count % 2)
                {
                    throw new Exception(string.Format("Layer [{0}]: uneven number of feature tag ids", layer.Name));
                }

                if (feat.Tags.Count > 0)
                {
                    int maxKeyIndex   = feat.Tags.Where((key, idx) => idx % 2 == 0).Max();
                    int maxValueIndex = feat.Tags.Where((key, idx) => (idx + 1) % 2 == 0).Max();

                    if (maxKeyIndex >= layer.Keys.Count)
                    {
                        throw new Exception(string.Format(
                                                "Layer [{0}]: maximum key index equal or greater number of key elements", layer.Name));
                    }

                    if (maxValueIndex >= layer.Values.Count)
                    {
                        throw new Exception(string.Format(
                                                "Layer [{0}]: maximum value index equal or greater number of value elements", layer.Name));
                    }
                }
            }

            return(feat);
        }
        private VectorTileLayer getLayer(byte[] data)
        {
            VectorTileLayer layer       = new VectorTileLayer(data);
            PbfReader       layerReader = new PbfReader(layer.Data);

            while (layerReader.NextByte())
            {
                int layerType = layerReader.Tag;
                if (_Validate)
                {
                    if (!ConstantsAsDictionary.LayerType.ContainsKey(layerType))
                    {
                        throw new Exception(string.Format("Unknown layer type: {0}", layerType));
                    }
                }

                switch ((LayerType)layerType)
                {
                case LayerType.Version:
                    ulong version = (ulong)layerReader.Varint();
                    layer.Version = version;
                    break;

                case LayerType.Name:
                    ulong strLength = (ulong)layerReader.Varint();
                    layer.Name = layerReader.GetString(strLength);
                    break;

                case LayerType.Extent:
                    layer.Extent = (ulong)layerReader.Varint();
                    break;

                case LayerType.Keys:
                    byte[] keyBuffer = layerReader.View();
                    string key       = Encoding.UTF8.GetString(keyBuffer, 0, keyBuffer.Length);
                    layer.Keys.Add(key);
                    break;

                case LayerType.Values:
                    byte[]    valueBuffer = layerReader.View();
                    PbfReader valReader   = new PbfReader(valueBuffer);
                    while (valReader.NextByte())
                    {
                        switch ((ValueType)valReader.Tag)
                        {
                        case ValueType.String:
                            byte[] stringBuffer = valReader.View();
                            string value        = Encoding.UTF8.GetString(stringBuffer, 0, stringBuffer.Length);
                            layer.Values.Add(value);
                            break;

                        case ValueType.Float:
                            float snglVal = valReader.GetFloat();
                            layer.Values.Add(snglVal);
                            break;

                        case ValueType.Double:
                            double dblVal = valReader.GetDouble();
                            layer.Values.Add(dblVal);
                            break;

                        case ValueType.Int:
                            long i64 = valReader.Varint();
                            layer.Values.Add(i64);
                            break;

                        case ValueType.UInt:
                            long u64 = valReader.Varint();
                            layer.Values.Add(u64);
                            break;

                        case ValueType.SInt:
                            long s64 = valReader.Varint();
                            layer.Values.Add(s64);
                            break;

                        case ValueType.Bool:
                            long b = valReader.Varint();
                            layer.Values.Add(b == 1);
                            break;

                        default:
                            throw new Exception(string.Format(
                                                    NumberFormatInfo.InvariantInfo
                                                    , "NOT IMPLEMENTED valueReader.Tag:{0} valueReader.WireType:{1}"
                                                    , valReader.Tag
                                                    , valReader.WireType
                                                    ));
                            //uncomment the following lines when not throwing!!
                            //valReader.Skip();
                            //break;
                        }
                    }

                    break;

                case LayerType.Features:
                    layer.AddFeatureData(layerReader.View());
                    break;

                default:
                    layerReader.Skip();
                    break;
                }
            }

            if (_Validate)
            {
                if (string.IsNullOrEmpty(layer.Name))
                {
                    throw new Exception("Layer has no name");
                }

                if (0 == layer.Version)
                {
                    throw new Exception(string.Format(
                                            "Layer [{0}] has invalid version. Only version 2.x of 'Mapbox Vector Tile Specification' (https://github.com/mapbox/vector-tile-spec) is supported.",
                                            layer.Name));
                }

                if (2 != layer.Version)
                {
                    throw new Exception(string.Format(
                                            "Layer [{0}] has invalid version: {1}. Only version 2.x of 'Mapbox Vector Tile Specification' (https://github.com/mapbox/vector-tile-spec) is supported.",
                                            layer.Name, layer.Version));
                }

                if (0 == layer.Extent)
                {
                    throw new Exception(string.Format("Layer [{0}] has no extent.", layer.Name));
                }

                if (0 == layer.FeatureCount())
                {
                    throw new Exception(string.Format("Layer [{0}] has no features.", layer.Name));
                }

                if (layer.Values.Count != layer.Values.Distinct().Count())
                {
                    throw new Exception(string.Format("Layer [{0}]: duplicate attribute values found", layer.Name));
                }
            }

            return(layer);
        }