/// <summary>
        ///     Creates a Geometry 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 &lt;Geometry Tagged Text&gt;.
        /// </param>
        /// <returns>Returns a Geometry specified by the next token in the stream.</returns>
        /// <remarks>
        ///     Exception is thrown if the coordinates used to create a Polygon
        ///     shell and holes do not form closed linestrings, or if an unexpected
        ///     token is encountered.
        /// </remarks>
        internal static Geometry ReadGeometryTaggedText(WktStreamTokenizer tokenizer)
        {
            tokenizer.NextToken();
            var      type = tokenizer.GetStringValue().ToUpper();
            Geometry geometry;

            switch (type)
            {
            case "POINT":
                geometry = ReadPointText(tokenizer);
                break;

            case "LINESTRING":
                geometry = ReadLineStringText(tokenizer);
                break;

            case "MULTILINESTRING":
                geometry = ReadMultiLineStringText(tokenizer);
                break;

            case "POLYGON":
                geometry = ReadPolygonText(tokenizer);
                break;

            case "MULTIPOLYGON":
                geometry = ReadMultiPolygonText(tokenizer);
                break;

            default:
                throw new NotImplementedException($"Geometrytype '{type}' is not supported.");
            }
            return(geometry);
        }
        /// <summary>
        ///     Returns the next word in the stream as uppercase text.
        /// </summary>
        /// <param name="tokenizer">
        ///     Tokenizer over a stream of text in Well-known Text
        ///     format. The next token must be a word.
        /// </param>
        /// <returns>Returns the next word in the stream as uppercase text.</returns>
        /// <remarks>
        ///     Exception is thrown if the next token is not a word.
        /// </remarks>
        private static string GetNextWord(WktStreamTokenizer tokenizer)
        {
            var type  = tokenizer.NextToken();
            var token = tokenizer.GetStringValue();

            if (type == TokenType.Number)
            {
                throw new Exception("Expected a number but got " + token);
            }
            if (type == TokenType.Word)
            {
                return(token.ToUpper());
            }
            if (token == "(")
            {
                return("(");
            }
            if (token == ")")
            {
                return(")");
            }
            if (token == ",")
            {
                return(",");
            }

            throw new Exception("Not a valid symbol in WKT format.");
        }
        /// <summary>
        ///     Returns the next ")" or "," in the stream.
        /// </summary>
        /// <param name="tokenizer">
        ///     tokenizer over a stream of text in Well-known Text
        ///     format. The next token must be ")" or ",".
        /// </param>
        /// <returns>Returns the next ")" or "," in the stream.</returns>
        /// <remarks>
        ///     ParseException is thrown if the next token is not ")" or ",".
        /// </remarks>
        private static string GetNextCloserOrComma(WktStreamTokenizer tokenizer)
        {
            tokenizer.NextToken();
            var nextWord = tokenizer.GetStringValue();

            if (nextWord == "," || nextWord == ")")
            {
                return(nextWord);
            }
            throw new Exception("Expected ')' or ',' but encountered '" + nextWord + "'");
        }
        /// <summary>
        ///     Returns the next "EMPTY" or "(" in the stream as uppercase text.
        /// </summary>
        /// <param name="tokenizer">
        ///     Tokenizer over a stream of text in Well-known Text
        ///     format. The next token must be "EMPTY" or "(".
        /// </param>
        /// <returns>
        ///     the next "EMPTY" or "(" in the stream as uppercase
        ///     text.
        /// </returns>
        /// <remarks>
        ///     ParseException is thrown if the next token is not "EMPTY" or "(".
        /// </remarks>
        private static string GetNextEmptyOrOpener(WktStreamTokenizer tokenizer)
        {
            tokenizer.NextToken();
            var nextWord = tokenizer.GetStringValue();

            if (nextWord == "EMPTY" || nextWord == "(")
            {
                return(nextWord);
            }

            throw new Exception("Expected 'EMPTY' or '(' but encountered '" + nextWord + "'");
        }
 /// <summary>
 ///     Returns the next number in the stream.
 /// </summary>
 /// <param name="tokenizer">
 ///     Tokenizer over a stream of text in Well-known text format.  The next token
 ///     must be a number.
 /// </param>
 /// <returns>Returns the next number in the stream.</returns>
 /// <remarks>
 ///     ParseException is thrown if the next token is not a number.
 /// </remarks>
 private static double GetNextNumber(WktStreamTokenizer tokenizer)
 {
     tokenizer.NextToken();
     return(tokenizer.GetNumericValue());
 }