GeometryFromWKT.cs
上传用户:sex100000
上传日期:2013-11-09
资源大小:1377k
文件大小:18k
源码类别:

GIS编程

开发平台:

C#

  1. // Copyright 2005, 2006 - Morten Nielsen (www.iter.dk)
  2. //
  3. // This file is part of SharpMap.
  4. // SharpMap is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation; either version 2 of the License, or
  7. // (at your option) any later version.
  8. // 
  9. // SharpMap is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. // GNU Lesser General Public License for more details.
  13. // You should have received a copy of the GNU Lesser General Public License
  14. // along with SharpMap; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  16. // SOURCECODE IS MODIFIED FROM ANOTHER WORK AND IS ORIGINALLY BASED ON GeoTools.NET:
  17. /*
  18.  *  Copyright (C) 2002 Urban Science Applications, Inc. 
  19.  *
  20.  *  This library is free software; you can redistribute it and/or
  21.  *  modify it under the terms of the GNU Lesser General Public
  22.  *  License as published by the Free Software Foundation; either
  23.  *  version 2.1 of the License, or (at your option) any later version.
  24.  *
  25.  *  This library is distributed in the hope that it will be useful,
  26.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  27.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  28.  *  Lesser General Public License for more details.
  29.  *
  30.  *  You should have received a copy of the GNU Lesser General Public
  31.  *  License along with this library; if not, write to the Free Software
  32.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  33.  *
  34.  */
  35. using System;
  36. using System.Collections.Generic;
  37. using System.Collections.ObjectModel;
  38. using System.Text;
  39. using SharpMap.Geometries;
  40. namespace SharpMap.Converters.WellKnownText
  41. {
  42. /// <summary>
  43. ///  Converts a Well-known Text representation to a <see cref="SharpMap.Geometries.Geometry"/> instance.
  44. /// </summary>
  45. /// <remarks>
  46. /// <para>The Well-Known Text (WKT) representation of Geometry is designed to exchange geometry data in ASCII form.</para>
  47. /// Examples of WKT representations of geometry objects are:
  48. /// <list type="table">
  49. /// <listheader><term>Geometry </term><description>WKT Representation</description></listheader>
  50. /// <item><term>A Point</term>
  51. /// <description>POINT(15 20)<br/> Note that point coordinates are specified with no separating comma.</description></item>
  52. /// <item><term>A LineString with four points:</term>
  53. /// <description>LINESTRING(0 0, 10 10, 20 25, 50 60)</description></item>
  54. /// <item><term>A Polygon with one exterior ring and one interior ring:</term>
  55. /// <description>POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7, 5 5))</description></item>
  56. /// <item><term>A MultiPoint with three Point values:</term>
  57. /// <description>MULTIPOINT(0 0, 20 20, 60 60)</description></item>
  58. /// <item><term>A MultiLineString with two LineString values:</term>
  59. /// <description>MULTILINESTRING((10 10, 20 20), (15 15, 30 15))</description></item>
  60. /// <item><term>A MultiPolygon with two Polygon values:</term>
  61. /// <description>MULTIPOLYGON(((0 0,10 0,10 10,0 10,0 0)),((5 5,7 5,7 7,5 7, 5 5)))</description></item>
  62. /// <item><term>A GeometryCollection consisting of two Point values and one LineString:</term>
  63. /// <description>GEOMETRYCOLLECTION(POINT(10 10), POINT(30 30), LINESTRING(15 15, 20 20))</description></item>
  64. /// </list>
  65. /// </remarks>
  66. public class GeometryFromWKT
  67. {
  68. /// <summary>
  69. /// Converts a Well-known text representation to a <see cref="SharpMap.Geometries.Geometry"/>.
  70. /// </summary>
  71. /// <param name="wellKnownText">A <see cref="SharpMap.Geometries.Geometry"/> tagged text string ( see the OpenGIS Simple Features Specification.</param>
  72. /// <returns>Returns a <see cref="SharpMap.Geometries.Geometry"/> specified by wellKnownText.  Throws an exception if there is a parsing problem.</returns>
  73. public static Geometry Parse(string wellKnownText)
  74. {
  75. // throws a parsing exception is there is a problem.
  76. System.IO.StringReader reader = new System.IO.StringReader(wellKnownText);
  77. return Parse(reader);
  78. }
  79. /// <summary>
  80. /// Converts a Well-known Text representation to a <see cref="SharpMap.Geometries.Geometry"/>.
  81. /// </summary>
  82. /// <param name="reader">A Reader which will return a Geometry Tagged Text
  83. /// string (see the OpenGIS Simple Features Specification)</param>
  84. /// <returns>Returns a <see cref="SharpMap.Geometries.Geometry"/> read from StreamReader. 
  85. /// An exception will be thrown if there is a parsing problem.</returns>
  86. public static Geometry Parse(System.IO.TextReader reader)
  87. {
  88. WktStreamTokenizer tokenizer = new WktStreamTokenizer(reader);
  89. return ReadGeometryTaggedText(tokenizer);
  90. }
  91. /// <summary>
  92. /// Returns the next array of Coordinates in the stream.
  93. /// </summary>
  94. /// <param name="tokenizer">Tokenizer over a stream of text in Well-known Text format.  The
  95. /// next element returned by the stream should be "(" (the beginning of "(x1 y1, x2 y2, ..., xn yn)" or
  96. /// "EMPTY".</param>
  97. /// <returns>The next array of Coordinates in the stream, or an empty array of "EMPTY" is the
  98. /// next element returned by the stream.</returns>
  99. private static Collection<SharpMap.Geometries.Point> GetCoordinates(WktStreamTokenizer tokenizer)
  100. {
  101. Collection<SharpMap.Geometries.Point> coordinates = new Collection<SharpMap.Geometries.Point>();
  102. string nextToken = GetNextEmptyOrOpener(tokenizer);
  103. if (nextToken=="EMPTY")
  104. return coordinates;
  105. SharpMap.Geometries.Point externalCoordinate = new SharpMap.Geometries.Point();
  106. SharpMap.Geometries.Point internalCoordinate = new SharpMap.Geometries.Point();
  107. externalCoordinate.X = GetNextNumber(tokenizer);
  108. externalCoordinate.Y = GetNextNumber(tokenizer);
  109. coordinates.Add(externalCoordinate);
  110. nextToken = GetNextCloserOrComma(tokenizer);
  111. while (nextToken==",")
  112. {
  113. internalCoordinate = new SharpMap.Geometries.Point();
  114. internalCoordinate.X = GetNextNumber(tokenizer);
  115. internalCoordinate.Y = GetNextNumber(tokenizer);
  116. coordinates.Add(internalCoordinate);
  117. nextToken = GetNextCloserOrComma(tokenizer);
  118. }
  119. return coordinates;
  120. }
  121. /// <summary>
  122. /// Returns the next number in the stream.
  123. /// </summary>
  124. /// <param name="tokenizer">Tokenizer over a stream of text in Well-known text format.  The next token
  125. /// must be a number.</param>
  126. /// <returns>Returns the next number in the stream.</returns>
  127. /// <remarks>
  128. /// ParseException is thrown if the next token is not a number.
  129. /// </remarks>
  130. private static double GetNextNumber(WktStreamTokenizer tokenizer)
  131. {
  132. tokenizer.NextToken();
  133. return tokenizer.GetNumericValue();
  134. }
  135. /// <summary>
  136. /// Returns the next "EMPTY" or "(" in the stream as uppercase text.
  137. /// </summary>
  138. /// <param name="tokenizer">Tokenizer over a stream of text in Well-known Text
  139. /// format. The next token must be "EMPTY" or "(".</param>
  140. /// <returns>the next "EMPTY" or "(" in the stream as uppercase
  141. /// text.</returns>
  142. /// <remarks>
  143. /// ParseException is thrown if the next token is not "EMPTY" or "(".
  144. /// </remarks>
  145. private static string GetNextEmptyOrOpener(WktStreamTokenizer tokenizer)
  146. {
  147. tokenizer.NextToken();
  148. string nextWord = tokenizer.GetStringValue();
  149. if (nextWord == "EMPTY" || nextWord == "(")
  150. return nextWord;
  151. throw new Exception("Expected 'EMPTY' or '(' but encountered '" + nextWord + "'");
  152. }
  153. /// <summary>
  154. /// Returns the next ")" or "," in the stream.
  155. /// </summary>
  156. /// <param name="tokenizer">tokenizer over a stream of text in Well-known Text
  157. /// format. The next token must be ")" or ",".</param>
  158. /// <returns>Returns the next ")" or "," in the stream.</returns>
  159. /// <remarks>
  160. /// ParseException is thrown if the next token is not ")" or ",".
  161. /// </remarks>
  162. private static string GetNextCloserOrComma(WktStreamTokenizer tokenizer)
  163. {
  164. tokenizer.NextToken();
  165. string nextWord = tokenizer.GetStringValue();
  166. if (nextWord == "," || nextWord == ")")
  167. {
  168. return nextWord;
  169. }
  170. throw new Exception("Expected ')' or ',' but encountered '" + nextWord + "'");
  171. }
  172. /// <summary>
  173. /// Returns the next ")" in the stream.
  174. /// </summary>
  175. /// <param name="tokenizer">Tokenizer over a stream of text in Well-known Text
  176. /// format. The next token must be ")".</param>
  177. /// <returns>Returns the next ")" in the stream.</returns>
  178. /// <remarks>
  179. /// ParseException is thrown if the next token is not ")".
  180. /// </remarks>
  181. private static string GetNextCloser(WktStreamTokenizer tokenizer)
  182. {
  183. string nextWord = GetNextWord(tokenizer);
  184. if (nextWord == ")")
  185. return nextWord;
  186. throw new Exception("Expected ')' but encountered '" + nextWord + "'");
  187. }
  188. /// <summary>
  189. /// Returns the next word in the stream as uppercase text.
  190. /// </summary>
  191. /// <param name="tokenizer">Tokenizer over a stream of text in Well-known Text
  192. /// format. The next token must be a word.</param>
  193. /// <returns>Returns the next word in the stream as uppercase text.</returns>
  194. /// <remarks>
  195. /// Exception is thrown if the next token is not a word.
  196. /// </remarks>
  197. private static string GetNextWord(WktStreamTokenizer tokenizer)
  198. {
  199. TokenType type = tokenizer.NextToken();
  200. string token = tokenizer.GetStringValue();
  201. if (type == TokenType.Number)
  202. throw new Exception("Expected a number but got " + token);
  203. else if (type == TokenType.Word)
  204. return token.ToUpper();
  205. else if (token == "(")
  206. return "(";
  207. else if (token == ")")
  208. return ")";
  209. else if (token == ",")
  210. return ",";
  211. throw new Exception("Not a valid symbol in WKT format.");
  212. }
  213. /// <summary>
  214. /// Creates a Geometry using the next token in the stream.
  215. /// </summary>
  216. /// <param name="tokenizer">Tokenizer over a stream of text in Well-known Text
  217. /// format. The next tokens must form a &lt;Geometry Tagged Text&gt;.</param>
  218. /// <returns>Returns a Geometry specified by the next token in the stream.</returns>
  219. /// <remarks>
  220. /// Exception is thrown if the coordinates used to create a Polygon
  221. /// shell and holes do not form closed linestrings, or if an unexpected
  222. /// token is encountered.
  223. /// </remarks>
  224. private static Geometry ReadGeometryTaggedText(WktStreamTokenizer tokenizer)
  225. {
  226. tokenizer.NextToken();
  227. string type = tokenizer.GetStringValue().ToUpper();
  228. Geometry geometry = null;
  229. switch (type)
  230. {
  231. case "POINT":
  232.     geometry = ReadPointText(tokenizer);
  233.     break;
  234. case "LINESTRING":
  235.     geometry = ReadLineStringText(tokenizer);
  236.     break;
  237. case "MULTIPOINT":
  238.     geometry = ReadMultiPointText(tokenizer);
  239.     break;
  240. case "MULTILINESTRING":
  241.     geometry = ReadMultiLineStringText(tokenizer);
  242.     break;
  243. case "POLYGON":
  244.     geometry = ReadPolygonText(tokenizer);
  245.     break;
  246. case "MULTIPOLYGON":
  247.     geometry = ReadMultiPolygonText(tokenizer);
  248.     break;
  249. case "GEOMETRYCOLLECTION":
  250.     geometry = ReadGeometryCollectionText(tokenizer);
  251.     break;
  252. default:
  253. throw new Exception(String.Format(SharpMap.Map.numberFormat_EnUS, "Geometrytype '{0}' is not supported.", type));
  254. }
  255. return geometry;
  256. }
  257. /// <summary>
  258. /// Creates a <see cref="MultiPolygon"/> using the next token in the stream.
  259. /// </summary>
  260. /// <param name="tokenizer">tokenizer over a stream of text in Well-known Text
  261. /// format. The next tokens must form a MultiPolygon.</param>
  262. /// <returns>a <code>MultiPolygon</code> specified by the next token in the 
  263. /// stream, or if if the coordinates used to create the <see cref="Polygon"/>
  264. /// shells and holes do not form closed linestrings.</returns>
  265. private static MultiPolygon ReadMultiPolygonText(WktStreamTokenizer tokenizer)
  266. {
  267. MultiPolygon polygons = new MultiPolygon();
  268. string nextToken = GetNextEmptyOrOpener(tokenizer);
  269. if (nextToken == "EMPTY")
  270. return polygons;
  271. Polygon polygon = ReadPolygonText(tokenizer);
  272. polygons.Polygons.Add(polygon);
  273. nextToken = GetNextCloserOrComma(tokenizer);
  274. while (nextToken == ",")
  275. {
  276. polygon = ReadPolygonText(tokenizer);
  277. polygons.Polygons.Add(polygon);
  278. nextToken = GetNextCloserOrComma(tokenizer);
  279. }
  280. return polygons;
  281. }
  282. /// <summary>
  283. /// Creates a Polygon using the next token in the stream.
  284. /// </summary>
  285. /// <param name="tokenizer">Tokenizer over a stream of text in Well-known Text
  286. ///  format. The next tokens must form a &lt;Polygon Text&gt;.</param>
  287. /// <returns>Returns a Polygon specified by the next token
  288. ///  in the stream</returns>
  289. ///  <remarks>
  290. ///  ParseException is thown if the coordinates used to create the Polygon
  291. ///  shell and holes do not form closed linestrings, or if an unexpected
  292. ///  token is encountered.
  293. ///  </remarks>
  294. private static Polygon ReadPolygonText(WktStreamTokenizer tokenizer)
  295. {
  296. Polygon pol = new Polygon();
  297. string nextToken = GetNextEmptyOrOpener(tokenizer);
  298. if (nextToken == "EMPTY")
  299. return pol;
  300. pol.ExteriorRing = new LinearRing(GetCoordinates(tokenizer));
  301. nextToken = GetNextCloserOrComma(tokenizer);
  302. while (nextToken == ",")
  303. {
  304. //Add holes
  305. pol.InteriorRings.Add(new LinearRing(GetCoordinates(tokenizer)));
  306. nextToken = GetNextCloserOrComma(tokenizer);
  307. }
  308. return pol;
  309. }
  310. /// <summary>
  311. /// Creates a Point using the next token in the stream.
  312. /// </summary>
  313. /// <param name="tokenizer">Tokenizer over a stream of text in Well-known Text
  314. /// format. The next tokens must form a &lt;Point Text&gt;.</param>
  315. /// <returns>Returns a Point specified by the next token in
  316. /// the stream.</returns>
  317. /// <remarks>
  318. /// ParseException is thrown if an unexpected token is encountered.
  319. /// </remarks>
  320. private static Point ReadPointText(WktStreamTokenizer tokenizer)
  321. {
  322. Point p = new Point();
  323. string nextToken = GetNextEmptyOrOpener(tokenizer);
  324. if (nextToken == "EMPTY")
  325. return p;
  326. p.X = GetNextNumber(tokenizer);
  327. p.Y = GetNextNumber(tokenizer);
  328. GetNextCloser(tokenizer);
  329. return p;
  330. }
  331. /// <summary>
  332. /// Creates a Point using the next token in the stream.
  333. /// </summary>
  334. /// <param name="tokenizer">Tokenizer over a stream of text in Well-known Text
  335. /// format. The next tokens must form a &lt;Point Text&gt;.</param>
  336. /// <returns>Returns a Point specified by the next token in
  337. /// the stream.</returns>
  338. /// <remarks>
  339. /// ParseException is thrown if an unexpected token is encountered.
  340. /// </remarks>
  341. private static MultiPoint ReadMultiPointText(WktStreamTokenizer tokenizer)
  342. {
  343. SharpMap.Geometries.MultiPoint mp = new MultiPoint();
  344. string nextToken = GetNextEmptyOrOpener(tokenizer);
  345. if (nextToken == "EMPTY")
  346. return mp;
  347. mp.Points.Add(new SharpMap.Geometries.Point(GetNextNumber(tokenizer),GetNextNumber(tokenizer)));
  348. nextToken = GetNextCloserOrComma(tokenizer);
  349. while (nextToken == ",")
  350. {
  351. mp.Points.Add(new SharpMap.Geometries.Point(GetNextNumber(tokenizer), GetNextNumber(tokenizer)));
  352. nextToken = GetNextCloserOrComma(tokenizer);
  353. }
  354. return mp;
  355. }
  356. /// <summary>
  357. /// Creates a <see cref="MultiLineString"/> using the next token in the stream. 
  358. /// </summary>
  359. /// <param name="tokenizer">tokenizer over a stream of text in Well-known Text format. The next tokens must form a MultiLineString Text</param>
  360. /// <returns>a <see cref="MultiLineString"/> specified by the next token in the stream</returns>
  361. private static MultiLineString ReadMultiLineStringText(WktStreamTokenizer tokenizer)
  362. {
  363. MultiLineString lines = new MultiLineString();
  364. string nextToken = GetNextEmptyOrOpener(tokenizer);
  365. if (nextToken == "EMPTY")
  366. return lines;
  367. lines.LineStrings.Add(ReadLineStringText(tokenizer));
  368. nextToken = GetNextCloserOrComma(tokenizer);
  369. while (nextToken == ",")
  370. {
  371. lines.LineStrings.Add(ReadLineStringText(tokenizer));
  372. nextToken = GetNextCloserOrComma(tokenizer);
  373. }
  374. return lines;
  375. }
  376. /// <summary>
  377. /// Creates a LineString using the next token in the stream.
  378. /// </summary>
  379. /// <param name="tokenizer">Tokenizer over a stream of text in Well-known Text format.  The next
  380. /// tokens must form a LineString Text.</param>
  381. /// <returns>Returns a LineString specified by the next token in the stream.</returns>
  382. /// <remarks>
  383. /// ParseException is thrown if an unexpected token is encountered.
  384. /// </remarks>
  385. private static LineString ReadLineStringText(WktStreamTokenizer tokenizer)
  386. {
  387. return new SharpMap.Geometries.LineString(GetCoordinates(tokenizer));
  388. }
  389. /// <summary>
  390. /// Creates a <see cref="GeometryCollection"/> using the next token in the stream.
  391. /// </summary>
  392. /// <param name="tokenizer"> Tokenizer over a stream of text in Well-known Text
  393. /// format. The next tokens must form a GeometryCollection Text.</param>
  394. /// <returns>
  395. /// A <see cref="GeometryCollection"/> specified by the next token in the stream.</returns>
  396. private static GeometryCollection ReadGeometryCollectionText(WktStreamTokenizer tokenizer)
  397. {
  398. GeometryCollection geometries = new GeometryCollection();
  399. string nextToken = GetNextEmptyOrOpener(tokenizer);
  400. if (nextToken.Equals("EMPTY"))
  401. return geometries;
  402. geometries.Collection.Add(ReadGeometryTaggedText(tokenizer));
  403. nextToken = GetNextCloserOrComma(tokenizer);
  404. while (nextToken.Equals(","))
  405. {
  406. geometries.Collection.Add(ReadGeometryTaggedText(tokenizer));
  407. nextToken = GetNextCloserOrComma(tokenizer);
  408. }
  409. return geometries;
  410. }        
  411. }
  412. }