/// <summary>
        /// Converts a PBF way into an OsmSharp-relation.
        /// </summary>
        public static OsmSharp.Relation DecodeRelation(PrimitiveBlock block, OsmSharp.IO.PBF.Relation pbfRelation,
                                                       OsmSharp.Relation relation)
        {
            // make sure old data is gone.
            if (relation.Members != null &&
                relation.Members.Length > 0)
            { // clear members.
                relation.Members = new RelationMember[pbfRelation.memids.Count];
            }
            if (relation.Tags != null)
            { // clear the tags collection.
                relation.Tags.Clear();
            }
            if (relation.Members == null)
            { // clear members.
                relation.Members = new RelationMember[pbfRelation.memids.Count];
            }
            if (relation.Tags == null)
            { // create tags collection.
                relation.Tags = new TagsCollection(pbfRelation.keys.Count);
            }

            // add nex stuff.
            relation.Id = pbfRelation.id;
            long memberId = 0;

            for (var i = 0; i < pbfRelation.types.Count; i++)
            {
                memberId = memberId + pbfRelation.memids[i];
                var role = System.Text.Encoding.UTF8.GetString(
                    block.stringtable.s[pbfRelation.roles_sid[i]]);

                var member = new OsmSharp.RelationMember();
                member.Id   = memberId;
                member.Role = role;
                switch (pbfRelation.types[i])
                {
                case Relation.MemberType.NODE:
                    member.Type = OsmSharp.OsmGeoType.Node;
                    break;

                case Relation.MemberType.WAY:
                    member.Type = OsmSharp.OsmGeoType.Way;
                    break;

                case Relation.MemberType.RELATION:
                    member.Type = OsmSharp.OsmGeoType.Relation;
                    break;
                }

                relation.Members[i] = member;
            }
            for (int i = 0; i < pbfRelation.keys.Count; i++)
            {
                string key   = System.Text.Encoding.UTF8.GetString(block.stringtable.s[(int)pbfRelation.keys[i]]);
                string value = System.Text.Encoding.UTF8.GetString(block.stringtable.s[(int)pbfRelation.vals[i]]);

                relation.Tags.Add(new Tag(key, value));
            }
            if (pbfRelation.info != null)
            { // read metadata if any.
                relation.ChangeSetId = pbfRelation.info.changeset;
                relation.TimeStamp   = Encoder.DecodeTimestamp(pbfRelation.info.timestamp, block.date_granularity);
                relation.UserId      = pbfRelation.info.uid;
                relation.UserName    = null;
                if (block.stringtable != null)
                {
                    relation.UserName = System.Text.Encoding.UTF8.GetString(block.stringtable.s[pbfRelation.info.user_sid]);
                }
                relation.Version = pbfRelation.info.version;
            }
            relation.Visible = true;

            return(relation);
        }
        /// <summary>
        /// Encodes an OsmSharp-relation into a PBF-relation.
        /// </summary>
        /// <returns></returns>
        public static OsmSharp.IO.PBF.Relation EncodeRelation(PrimitiveBlock block, Dictionary <string, int> reverseStringTable, OsmSharp.Relation relation)
        {
            var pbfRelation = new OsmSharp.IO.PBF.Relation();

            pbfRelation.id   = relation.Id.Value;
            pbfRelation.info = new Info();
            if (relation.ChangeSetId.HasValue)
            {
                pbfRelation.info.changeset = relation.ChangeSetId.Value;
            }
            if (relation.TimeStamp.HasValue)
            {
                pbfRelation.info.timestamp = Encoder.EncodeTimestamp(relation.TimeStamp.Value, block.date_granularity);
            }
            if (relation.UserId.HasValue)
            {
                pbfRelation.info.uid = (int)relation.UserId.Value;
            }
            pbfRelation.info.user_sid = Encoder.EncodeString(block, reverseStringTable, relation.UserName);
            pbfRelation.info.version  = 0;
            if (relation.Version.HasValue)
            {
                pbfRelation.info.version = (int)relation.Version.Value;
            }

            if (relation.Tags != null)
            {
                foreach (var tag in relation.Tags)
                {
                    pbfRelation.keys.Add((uint)Encoder.EncodeString(block, reverseStringTable, tag.Key));
                    pbfRelation.vals.Add((uint)Encoder.EncodeString(block, reverseStringTable, tag.Value));
                }
            }

            if (relation.Members != null &&
                relation.Members.Length > 0)
            {
                pbfRelation.memids.Add(relation.Members[0].Id);
                pbfRelation.roles_sid.Add(Encoder.EncodeString(block, reverseStringTable, relation.Members[0].Role));
                switch (relation.Members[0].Type)
                {
                case OsmGeoType.Node:
                    pbfRelation.types.Add(Relation.MemberType.NODE);
                    break;

                case OsmGeoType.Way:
                    pbfRelation.types.Add(Relation.MemberType.WAY);
                    break;

                case OsmGeoType.Relation:
                    pbfRelation.types.Add(Relation.MemberType.RELATION);
                    break;
                }
                for (var i = 1; i < relation.Members.Length; i++)
                {
                    pbfRelation.memids.Add(relation.Members[i].Id -
                                           relation.Members[i - 1].Id);
                    pbfRelation.roles_sid.Add(Encoder.EncodeString(block, reverseStringTable, relation.Members[i].Role));
                    switch (relation.Members[i].Type)
                    {
                    case OsmGeoType.Node:
                        pbfRelation.types.Add(Relation.MemberType.NODE);
                        break;

                    case OsmGeoType.Way:
                        pbfRelation.types.Add(Relation.MemberType.WAY);
                        break;

                    case OsmGeoType.Relation:
                        pbfRelation.types.Add(Relation.MemberType.RELATION);
                        break;
                    }
                }
            }
            return(pbfRelation);
        }
        /// <summary>
        /// Encodes the nodes into the given block.
        /// </summary>
        public static void Encode(this PrimitiveBlock block, Dictionary <string, int> reverseStringTable, List <OsmGeo> osmGeos)
        {
            var groupIdx  = 0;
            var i         = 0;
            var nodeCount = 0;

            if (block.stringtable != null &&
                block.stringtable.s != null)
            {
                block.stringtable.s.Clear();
            }
            while (i < osmGeos.Count)
            {
                PrimitiveGroup group = null;
                if (groupIdx < block.primitivegroup.Count)
                { // get the existing group and clear it's data.
                    group = block.primitivegroup[groupIdx];
                    if (group == null)
                    {
                        group = new PrimitiveGroup();
                    }
                    if (group.dense != null)
                    {
                        if (group.dense.denseinfo != null)
                        {
                            if (group.dense.denseinfo.changeset != null)
                            {
                                group.dense.denseinfo.changeset.Clear();
                            }
                            if (group.dense.denseinfo.timestamp != null)
                            {
                                group.dense.denseinfo.timestamp.Clear();
                            }
                            if (group.dense.denseinfo.uid != null)
                            {
                                group.dense.denseinfo.uid.Clear();
                            }
                            if (group.dense.denseinfo.user_sid != null)
                            {
                                group.dense.denseinfo.user_sid.Clear();
                            }
                            if (group.dense.denseinfo.version != null)
                            {
                                group.dense.denseinfo.version.Clear();
                            }
                        }
                        if (group.dense.id != null)
                        {
                            group.dense.id.Clear();
                        }
                        if (group.dense.keys_vals != null)
                        {
                            group.dense.keys_vals.Clear();
                        }
                        if (group.dense.lat != null)
                        {
                            group.dense.lat.Clear();
                        }
                        if (group.dense.lon != null)
                        {
                            group.dense.lon.Clear();
                        }
                    }
                    if (group.changesets != null)
                    {
                        group.changesets.Clear();
                    }
                    //if (group.nodes != null) { group.nodes.Clear(); }
                    if (group.ways != null)
                    {
                        group.ways.Clear();
                    }
                    if (group.relations != null)
                    {
                        group.relations.Clear();
                    }
                }
                else
                { // add a new group.
                    group = new PrimitiveGroup();
                    block.primitivegroup.Add(group);
                }

                // build group.
                var groupType = osmGeos[i].Type;
                var current   = osmGeos[i];
                while (i < osmGeos.Count &&
                       osmGeos[i].Type == groupType)
                {
                    switch (groupType)
                    {
                    case OsmGeoType.Node:
                        if (group.nodes.Count > nodeCount)
                        {     // overwrite existing.
                            Encoder.EncodeNode(block, reverseStringTable, group.nodes[nodeCount], osmGeos[i] as OsmSharp.Node);
                        }
                        else
                        {     // write new.
                            group.nodes.Add(Encoder.EncodeNode(block, reverseStringTable, osmGeos[i] as OsmSharp.Node));
                        }
                        nodeCount++;
                        break;

                    case OsmGeoType.Way:
                        group.ways.Add(Encoder.EncodeWay(block, reverseStringTable, osmGeos[i] as OsmSharp.Way));
                        break;

                    case OsmGeoType.Relation:
                        group.relations.Add(Encoder.EncodeRelation(block, reverseStringTable, osmGeos[i] as OsmSharp.Relation));
                        break;
                    }
                    i++;
                }


                // remove obsolete nodes.
                if (group.nodes != null)
                {
                    while (nodeCount < group.nodes.Count)
                    {
                        group.nodes.RemoveAt(nodeCount);
                    }
                }

                // move to the next group.
                groupIdx++;
            }

            while (groupIdx < block.primitivegroup.Count)
            {
                block.primitivegroup.RemoveAt(groupIdx);
            }
        }