/// <summary>
        /// Creates a <see cref="MultiLineString"/> using the next token in the stream. 
        /// </summary>
        /// <param name="tokenizer">tokenizer over a stream of text in Well-known Text format. The next tokens must form a MultiLineString Text</param>
        /// <returns>a <see cref="MultiLineString"/> specified by the next token in the stream</returns>
        private static MultiLineString ReadMultiLineStringText(WktStreamTokenizer tokenizer)
        {
            var arrlines = new List<ILineString>();

            string nextToken = GetNextEmptyOrOpener(tokenizer);
            if (nextToken == "EMPTY")
                return new MultiLineString(arrlines.ToArray());

            arrlines.Add(ReadLineStringText(tokenizer));
            nextToken = GetNextCloserOrComma(tokenizer);
            while (nextToken == ",")
            {
                arrlines.Add(ReadLineStringText(tokenizer));
                nextToken = GetNextCloserOrComma(tokenizer);
            }

            MultiLineString lines = new MultiLineString(arrlines.ToArray());
            return lines;
        }
        private Feature ConvertRelation(CompleteRelation relation)
        {
            if (IsMultipolygon(relation))
            {
                return ConvertToMultipolygon(relation);
            }

            var nodes = relation.Members.Select(m => m.Member).OfType<Node>().ToList();
            if (nodes.Any())
            {
                var multiPoint = new MultiPoint(nodes.Select(n => new Point(ConvertNode(n)) as IPoint).ToArray());
                return new Feature(multiPoint, ConvertTags(relation.Tags, relation.Id));
            }

            var geometries = GetGeometriesFromWays(GetAllWays(relation));
            if (!geometries.Any())
            {
                return null;
            }
            var jointLines = geometries.OfType<ILineString>().ToList();
            jointLines.AddRange(geometries.OfType<Polygon>().Select(p => new LineString(p.Coordinates) as ILineString));
            var multiLineString = new MultiLineString(jointLines.ToArray());
            return new Feature(multiLineString, ConvertTags(relation.Tags, relation.Id));
        }
        private static MultiLineString CreateWKBMultiLineString(BinaryReader reader, WkbByteOrder byteOrder)
        {
            // Get the number of linestrings in this multilinestring.
            int numLineStrings = (int) ReadUInt32(reader, byteOrder);
            var lineStrings = new ILineString[numLineStrings];

            // Loop on the number of linestrings.
            for (int i = 0; i < numLineStrings; i++)
            {
                // Read linestring header
                reader.ReadByte();
                ReadUInt32(reader, byteOrder);

                // Create the next linestring and add it to the array.
                lineStrings[i] = CreateWKBLineString(reader, byteOrder);
            }

            // Create a new array for the linestrings .
            MultiLineString mline = new MultiLineString(lineStrings);

            // Create and return the MultiLineString.
            return mline;
        }
        /// <summary>
        /// This method produces instances of type <see cref="MultiLineString"/>.
        /// </summary>
        /// <returns>The created geometries</returns>
        internal override Collection<SimpleGisShape> createGeometries()
        {
            SimpleGisShape shp = null;

            IPathNode multiLineStringNode = new PathNode(_GMLNS, "MultiLineString", (NameTable)_xmlReader.NameTable);
            IPathNode multiCurveNode = new PathNode(_GMLNS, "MultiCurve", (NameTable)_xmlReader.NameTable);
            IPathNode multiLineStringNodeAlt = new AlternativePathNodesCollection(multiLineStringNode, multiCurveNode);
            IPathNode lineStringMemberNode = new PathNode(_GMLNS, "lineStringMember", (NameTable)_xmlReader.NameTable);
            IPathNode curveMemberNode = new PathNode(_GMLNS, "curveMember", (NameTable)_xmlReader.NameTable);
            IPathNode lineStringMemberNodeAlt = new AlternativePathNodesCollection(lineStringMemberNode, curveMemberNode);
            string[] labelValue = new string[1];

            try
            {
                ParseBoundingBox();

                // Reading the entire feature's node makes it possible to collect label values that may appear before or after the geometry property
                while ((_featureReader = GetSubReaderOf(_xmlReader, null, _featureNode)) != null)
                {
                    while ((_geomReader = GetSubReaderOf(_featureReader, null, _propertyNode)) != null)
                    {
                        bool isSelected;
                        int uid;
                        List<string> ll = ParseProperties(_geomReader, out isSelected, out uid);

                        _geomReader = GetSubReaderOf(_featureReader, labelValue, multiLineStringNodeAlt, lineStringMemberNodeAlt);

                        GeometryFactory geomFactory = new LineStringFactory(_geomReader, _featureTypeInfo, _fieldNames);
                        Collection<SimpleGisShape> shpLineStrings = geomFactory.createGeometries();

                        var lineStrings = new List<ILineString>();

                        foreach (var lineString in shpLineStrings)
                        {
                            lineStrings.Add(lineString.Geometry as ILineString);
                        }
                        
                        MultiLineString multiLineString = new MultiLineString(lineStrings.ToArray());
                        shp = new SimpleGisShape(multiLineString);
                        shp.IsSelected = isSelected;
                        shp.UID = uid;

                        _shapes.Add(shp);
                        FillShapeFields(shp, ll);
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return _shapes;
        }
        /// <summary>
        /// Writes a multilinestring.
        /// </summary>
        /// <param name="mls">The multilinestring to be written.</param>
        /// <param name="bWriter">Stream to write to.</param>
        /// <param name="byteorder">Byte order</param>
        private static void WriteMultiLineString(MultiLineString mls, BinaryWriter bWriter, WkbByteOrder byteorder)
        {
            //Write the number of linestrings.
            WriteUInt32((uint) mls.Geometries.Length, bWriter, byteorder);

            //Loop on the number of linestrings.
            foreach (var g in mls.Geometries)
            {
                var ls = g as LineString;
                if (ls == null) throw new ArgumentNullException("Linestring in a multilinestring");

                //Write LineString Header
                bWriter.Write((byte) byteorder);
                WriteUInt32((uint) WKBGeometryType.wkbLineString, bWriter, byteorder);
                //Write each linestring.
                WriteLineString(ls, bWriter, byteorder);
            }
        }
  private DataTable GetShapeTable(string sql, OgcGeometryType geometryType)
  {
    DataTable table = null;

    using (OleDbConnection connection = AppContext.GetDatabaseConnection())
    {
      table = new DataTable();

      using (OleDbDataAdapter adapter = new OleDbDataAdapter(sql, connection))
      {
        adapter.Fill(table);
      }

      table.Columns["Shape"].ColumnName = "ShapeString";

      switch (geometryType)
      {
        case OgcGeometryType.Point: table.Columns.Add("Shape", typeof(IPoint)); break;
        case OgcGeometryType.LineString: table.Columns.Add("Shape", typeof(IMultiLineString)); break;
        case OgcGeometryType.Polygon: table.Columns.Add("Shape", typeof(IMultiPolygon)); break;
      }

      WKTReader wktReader = new WKTReader();

      foreach (DataRow row in table.Rows)
      {
        switch (geometryType)
        {
          case OgcGeometryType.Point:
            row["Shape"] = (IPoint)wktReader.Read((string)row["ShapeString"]);
            break;

          case OgcGeometryType.LineString:
            ILineString lineString = (ILineString)wktReader.Read((string)row["ShapeString"]);
            IMultiLineString multiLineString = new MultiLineString(new ILineString[] { lineString });
            row["Shape"] = multiLineString;
            break;

          case OgcGeometryType.Polygon:
            IPolygon polygon = (IPolygon)wktReader.Read((string)row["ShapeString"]);
            IMultiPolygon multiPolygon = new MultiPolygon(new IPolygon[] { polygon });
            row["Shape"] = multiPolygon;
            break;
        }
      }

      table.Columns.Remove("ShapeString");
    }

    return table;
  }
 /// <summary>
 /// Converts a MultiLineString to &lt;MultiLineString Text&gt;
 /// format, then Appends it to the writer.
 /// </summary>
 /// <param name="multiLineString">The MultiLineString to process.</param>
 /// <param name="writer">The output stream writer to Append to.</param>
 private static void AppendMultiLineStringText(MultiLineString multiLineString, StringWriter writer)
 {
     if (multiLineString == null || multiLineString.IsEmpty)
         writer.Write("EMPTY");
     else
     {
         writer.Write("(");
         for (int i = 0; i < multiLineString.Geometries.Length; i++)
         {
             if (i > 0)
                 writer.Write(", ");
             AppendLineStringText(multiLineString.Geometries[i] as LineString, writer);
         }
         writer.Write(")");
     }
 }
 /// <summary>
 /// Converts a MultiLineString to &lt;MultiLineString Tagged
 /// Text&gt; format, then Appends it to the writer.
 /// </summary>
 /// <param name="multiLineString">The MultiLineString to process</param>
 /// <param name="writer">The output stream writer to Append to.</param>
 private static void AppendMultiLineStringTaggedText(MultiLineString multiLineString, StringWriter writer)
 {
     writer.Write("MULTILINESTRING ");
     AppendMultiLineStringText(multiLineString, writer);
 }