GeoJSONParser.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. import Coordinate from '../geom/Coordinate'
  2. import GeometryFactory from '../geom/GeometryFactory'
  3. const geometryTypes = ['Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon']
  4. /**
  5. * Class for reading and writing Well-Known Text.Create a new parser for GeoJSON
  6. * NOTE: Adapted from OpenLayers 2.11 implementation.
  7. */
  8. /**
  9. * Create a new parser for GeoJSON
  10. *
  11. * @param {GeometryFactory} geometryFactory
  12. * @return An instance of GeoJsonParser.
  13. * @constructor
  14. * @private
  15. */
  16. export default class GeoJSONParser {
  17. constructor(geometryFactory) {
  18. this.geometryFactory = geometryFactory || new GeometryFactory()
  19. }
  20. /**
  21. * Deserialize a GeoJSON object and return the Geometry or Feature(Collection) with JSTS Geometries
  22. *
  23. * @param {}
  24. * A GeoJSON object.
  25. * @return {} A Geometry instance or object representing a Feature(Collection) with Geometry instances.
  26. * @private
  27. */
  28. read(json) {
  29. let obj
  30. if (typeof json === 'string')
  31. obj = JSON.parse(json)
  32. else obj = json
  33. const type = obj.type
  34. if (!parse[type]) throw new Error('Unknown GeoJSON type: ' + obj.type)
  35. if (geometryTypes.indexOf(type) !== -1)
  36. return parse[type].call(this, obj.coordinates)
  37. else if (type === 'GeometryCollection') return parse[type].call(this, obj.geometries)
  38. // feature or feature collection
  39. return parse[type].call(this, obj)
  40. }
  41. /**
  42. * Serialize a Geometry object into GeoJSON
  43. *
  44. * @param {Geometry}
  45. * geometry A Geometry or array of Geometries.
  46. * @return {Object} A GeoJSON object represting the input Geometry/Geometries.
  47. * @private
  48. */
  49. write(geometry) {
  50. const type = geometry.getGeometryType()
  51. if (!extract[type]) throw new Error('Geometry is not supported')
  52. return extract[type].call(this, geometry)
  53. }
  54. }
  55. const parse = {
  56. /**
  57. * Parse a GeoJSON Feature object
  58. *
  59. * @param {Object}
  60. * obj Object to parse.
  61. *
  62. * @return {Object} Feature with geometry/bbox converted to JSTS Geometries.
  63. */
  64. Feature: function(obj) {
  65. const feature = {}
  66. for (const key in obj) feature[key] = obj[key]
  67. if (obj.geometry) {
  68. const type = obj.geometry.type
  69. if (!parse[type]) throw new Error('Unknown GeoJSON type: ' + obj.type)
  70. feature.geometry = this.read(obj.geometry)
  71. }
  72. if (obj.bbox) feature.bbox = parse.bbox.call(this, obj.bbox)
  73. return feature
  74. },
  75. /**
  76. * Parse a GeoJSON FeatureCollection object
  77. *
  78. * @param {Object}
  79. * obj Object to parse.
  80. *
  81. * @return {Object} FeatureCollection with geometry/bbox converted to JSTS Geometries.
  82. */
  83. FeatureCollection: function(obj) {
  84. const featureCollection = {}
  85. if (obj.features) {
  86. featureCollection.features = []
  87. for (let i = 0; i < obj.features.length; ++i) featureCollection.features.push(this.read(obj.features[i]))
  88. }
  89. if (obj.bbox) featureCollection.bbox = this.parse.bbox.call(this, obj.bbox)
  90. return featureCollection
  91. },
  92. /**
  93. * Convert the ordinates in an array to an array of Coordinates
  94. *
  95. * @param {Array}
  96. * array Array with {Number}s.
  97. *
  98. * @return {Array} Array with Coordinates.
  99. */
  100. coordinates: function(array) {
  101. const coordinates = []
  102. for (let i = 0; i < array.length; ++i) {
  103. const sub = array[i]
  104. coordinates.push(new Coordinate(...sub))
  105. }
  106. return coordinates
  107. },
  108. /**
  109. * Convert the bbox to a LinearRing
  110. *
  111. * @param {Array}
  112. * array Array with [xMin, yMin, xMax, yMax].
  113. *
  114. * @return {Array} Array with Coordinates.
  115. */
  116. bbox: function(array) {
  117. return this.geometryFactory.createLinearRing([
  118. new Coordinate(array[0], array[1]),
  119. new Coordinate(array[2], array[1]),
  120. new Coordinate(array[2], array[3]),
  121. new Coordinate(array[0], array[3]),
  122. new Coordinate(array[0], array[1])
  123. ])
  124. },
  125. /**
  126. * Convert an Array with ordinates to a Point
  127. *
  128. * @param {Array}
  129. * array Array with ordinates.
  130. *
  131. * @return {Point} Point.
  132. */
  133. Point: function(array) {
  134. const coordinate = new Coordinate(...array)
  135. return this.geometryFactory.createPoint(coordinate)
  136. },
  137. /**
  138. * Convert an Array with coordinates to a MultiPoint
  139. *
  140. * @param {Array}
  141. * array Array with coordinates.
  142. *
  143. * @return {MultiPoint} MultiPoint.
  144. */
  145. MultiPoint: function(array) {
  146. const points = []
  147. for (let i = 0; i < array.length; ++i) points.push(parse.Point.call(this, array[i]))
  148. return this.geometryFactory.createMultiPoint(points)
  149. },
  150. /**
  151. * Convert an Array with coordinates to a LineString
  152. *
  153. * @param {Array}
  154. * array Array with coordinates.
  155. *
  156. * @return {LineString} LineString.
  157. */
  158. LineString: function(array) {
  159. const coordinates = parse.coordinates.call(this, array)
  160. return this.geometryFactory.createLineString(coordinates)
  161. },
  162. /**
  163. * Convert an Array with coordinates to a MultiLineString
  164. *
  165. * @param {Array}
  166. * array Array with coordinates.
  167. *
  168. * @return {MultiLineString} MultiLineString.
  169. */
  170. MultiLineString: function(array) {
  171. const lineStrings = []
  172. for (let i = 0; i < array.length; ++i) lineStrings.push(parse.LineString.call(this, array[i]))
  173. return this.geometryFactory.createMultiLineString(lineStrings)
  174. },
  175. /**
  176. * Convert an Array to a Polygon
  177. *
  178. * @param {Array}
  179. * array Array with shell and holes.
  180. *
  181. * @return {Polygon} Polygon.
  182. */
  183. Polygon: function(array) {
  184. const shellCoordinates = parse.coordinates.call(this, array[0])
  185. const shell = this.geometryFactory.createLinearRing(shellCoordinates)
  186. const holes = []
  187. for (let i = 1; i < array.length; ++i) {
  188. const hole = array[i]
  189. const coordinates = parse.coordinates.call(this, hole)
  190. const linearRing = this.geometryFactory.createLinearRing(coordinates)
  191. holes.push(linearRing)
  192. }
  193. return this.geometryFactory.createPolygon(shell, holes)
  194. },
  195. /**
  196. * Convert an Array to a MultiPolygon
  197. *
  198. * @param {Array}
  199. * array Array of arrays with shell and rings.
  200. *
  201. * @return {MultiPolygon} MultiPolygon.
  202. */
  203. MultiPolygon: function(array) {
  204. const polygons = []
  205. for (let i = 0; i < array.length; ++i) {
  206. const polygon = array[i]
  207. polygons.push(parse.Polygon.call(this, polygon))
  208. }
  209. return this.geometryFactory.createMultiPolygon(polygons)
  210. },
  211. /**
  212. * Convert an Array to a GeometryCollection
  213. *
  214. * @param {Array}
  215. * array Array of GeoJSON geometries.
  216. *
  217. * @return {GeometryCollection} GeometryCollection.
  218. */
  219. GeometryCollection: function(array) {
  220. const geometries = []
  221. for (let i = 0; i < array.length; ++i) {
  222. const geometry = array[i]
  223. geometries.push(this.read(geometry))
  224. }
  225. return this.geometryFactory.createGeometryCollection(geometries)
  226. }
  227. }
  228. const extract = {
  229. /**
  230. * Convert a Coordinate to an Array
  231. *
  232. * @param {Coordinate}
  233. * coordinate Coordinate to convert.
  234. *
  235. * @return {Array} Array of ordinates.
  236. */
  237. coordinate: function(coordinate) {
  238. const a = [coordinate.x, coordinate.y]
  239. if (coordinate.z)
  240. a.push(coordinate.z)
  241. if (coordinate.m)
  242. a.push(coordinate.m)
  243. return a
  244. },
  245. /**
  246. * Convert a Point to a GeoJSON object
  247. *
  248. * @param {Point}
  249. * point Point to convert.
  250. *
  251. * @return {Array} Array of 2 ordinates (paired to a coordinate).
  252. */
  253. Point: function(point) {
  254. const array = extract.coordinate.call(this, point.getCoordinate())
  255. return {
  256. type: 'Point',
  257. coordinates: array
  258. }
  259. },
  260. /**
  261. * Convert a MultiPoint to a GeoJSON object
  262. *
  263. * @param {MultiPoint}
  264. * multipoint MultiPoint to convert.
  265. *
  266. * @return {Array} Array of coordinates.
  267. */
  268. MultiPoint: function(multipoint) {
  269. const array = []
  270. for (let i = 0; i < multipoint._geometries.length; ++i) {
  271. const point = multipoint._geometries[i]
  272. const geoJson = extract.Point.call(this, point)
  273. array.push(geoJson.coordinates)
  274. }
  275. return {
  276. type: 'MultiPoint',
  277. coordinates: array
  278. }
  279. },
  280. /**
  281. * Convert a LineString to a GeoJSON object
  282. *
  283. * @param {LineString}
  284. * linestring LineString to convert.
  285. *
  286. * @return {Array} Array of coordinates.
  287. */
  288. LineString: function(linestring) {
  289. const array = []
  290. const coordinates = linestring.getCoordinates()
  291. for (let i = 0; i < coordinates.length; ++i) {
  292. const coordinate = coordinates[i]
  293. array.push(extract.coordinate.call(this, coordinate))
  294. }
  295. return {
  296. type: 'LineString',
  297. coordinates: array
  298. }
  299. },
  300. /**
  301. * Convert a MultiLineString to a GeoJSON object
  302. *
  303. * @param {MultiLineString}
  304. * multilinestring MultiLineString to convert.
  305. *
  306. * @return {Array} Array of Array of coordinates.
  307. */
  308. MultiLineString: function(multilinestring) {
  309. const array = []
  310. for (let i = 0; i < multilinestring._geometries.length; ++i) {
  311. const linestring = multilinestring._geometries[i]
  312. const geoJson = extract.LineString.call(this, linestring)
  313. array.push(geoJson.coordinates)
  314. }
  315. return {
  316. type: 'MultiLineString',
  317. coordinates: array
  318. }
  319. },
  320. /**
  321. * Convert a Polygon to a GeoJSON object
  322. *
  323. * @param {Polygon}
  324. * polygon Polygon to convert.
  325. *
  326. * @return {Array} Array with shell, holes.
  327. */
  328. Polygon: function(polygon) {
  329. const array = []
  330. const shellGeoJson = extract.LineString.call(this, polygon._shell)
  331. array.push(shellGeoJson.coordinates)
  332. for (let i = 0; i < polygon._holes.length; ++i) {
  333. const hole = polygon._holes[i]
  334. const holeGeoJson = extract.LineString.call(this, hole)
  335. array.push(holeGeoJson.coordinates)
  336. }
  337. return {
  338. type: 'Polygon',
  339. coordinates: array
  340. }
  341. },
  342. /**
  343. * Convert a MultiPolygon to a GeoJSON object
  344. *
  345. * @param {MultiPolygon}
  346. * multipolygon MultiPolygon to convert.
  347. *
  348. * @return {Array} Array of polygons.
  349. */
  350. MultiPolygon: function(multipolygon) {
  351. const array = []
  352. for (let i = 0; i < multipolygon._geometries.length; ++i) {
  353. const polygon = multipolygon._geometries[i]
  354. const geoJson = extract.Polygon.call(this, polygon)
  355. array.push(geoJson.coordinates)
  356. }
  357. return {
  358. type: 'MultiPolygon',
  359. coordinates: array
  360. }
  361. },
  362. /**
  363. * Convert a GeometryCollection to a GeoJSON object
  364. *
  365. * @param {GeometryCollection}
  366. * collection GeometryCollection to convert.
  367. *
  368. * @return {Array} Array of geometries.
  369. */
  370. GeometryCollection: function(collection) {
  371. const array = []
  372. for (let i = 0; i < collection._geometries.length; ++i) {
  373. const geometry = collection._geometries[i]
  374. const type = geometry.getGeometryType()
  375. array.push(extract[type].call(this, geometry))
  376. }
  377. return {
  378. type: 'GeometryCollection',
  379. geometries: array
  380. }
  381. }
  382. }