PageRenderTime 37ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/shapefile.js

http://github.com/wavded/js-shapefile-to-geojson
JavaScript | 375 lines | 292 code | 70 blank | 13 comment | 42 complexity | a8310a4c18ce2a1313facc1575e591cb MD5 | raw file
  1. (function(window,undefined){
  2. if(window.document && window.Worker){
  3. var worker = null;
  4. var Shapefile = function(o, callback){
  5. var
  6. t = this,
  7. o = typeof o == "string" ? {shp: o} : o
  8. if (!worker) {
  9. var path = (o.jsRoot || "") + "shapefile.js"
  10. var w = worker = this.worker = new Worker(path)
  11. } else {
  12. var w = worker
  13. }
  14. w.onmessage = function(e){
  15. t.data = e.date
  16. if(callback) callback(e.data)
  17. }
  18. w.postMessage(["Load", o])
  19. if(o.dbf) this.dbf = new DBF(o.dbf,function(data){
  20. w.postMessage(["Add DBF Attributes", data])
  21. })
  22. }
  23. window["Shapefile"] = Shapefile
  24. return
  25. }
  26. var IN_WORKER = !window.document
  27. if (IN_WORKER) {
  28. importScripts('stream.js')
  29. onmessage = function(e){
  30. switch (e.data[0]) {
  31. case "Load":
  32. window.shapefile = new Shapefile(e.data[1])
  33. break
  34. case "Add DBF Attributes":
  35. window.shapefile.addDBFDataToGeoJSON(e.data[1])
  36. window.shapefile._postMessage()
  37. break
  38. default:
  39. }
  40. };
  41. }
  42. var SHAPE_TYPES = {
  43. "0": "Null Shape",
  44. "1": "Point", // standard shapes
  45. "3": "PolyLine",
  46. "5": "Polygon",
  47. "8": "MultiPoint",
  48. "11": "PointZ", // 3d shapes
  49. "13": "PolyLineZ",
  50. "15": "PolygonZ",
  51. "18": "MultiPointZ",
  52. "21": "PointM", // user-defined measurement shapes
  53. "23": "PolyLineM",
  54. "25": "PolygonM",
  55. "28": "MultiPointM",
  56. "31": "MultiPatch"
  57. }
  58. var Shapefile = function(o,callback){
  59. var o = typeof o == "string" ? {shp: o} : o
  60. this.callback = callback
  61. if (!!(o.shp.lastModifiedDate || o.shp.lastModified))
  62. this.handleFile(o);
  63. else
  64. this.handleUri(o);
  65. }
  66. Shapefile.prototype = {
  67. constructor: Shapefile,
  68. handleUri: function(o) {
  69. var xhr = new XMLHttpRequest(),
  70. that = this
  71. xhr.open("GET", o.shp, false)
  72. xhr.overrideMimeType("text/plain; charset=x-user-defined")
  73. xhr.send()
  74. if(200 != xhr.status)
  75. throw "Unable to load " + o.shp + " status: " + xhr.status
  76. this.url = o.shp
  77. this.stream = new Gordon.Stream(xhr.responseText)
  78. this.readFileHeader()
  79. this.readRecords()
  80. this.formatIntoGeoJson()
  81. if(o.dbf) this.dbf = IN_WORKER ?
  82. null :
  83. new DBF(o.dbf,function(data){
  84. that.addDBFDataToGeoJSON(data)
  85. that._postMessage()
  86. })
  87. else this._postMessage()
  88. },
  89. handleFile: function(o) {
  90. this.options = o
  91. if (!!window.FileReader) {
  92. var reader = new FileReader();
  93. } else {
  94. var reader = new FileReaderSync();
  95. }
  96. reader.onload = (function(that){
  97. return function(e){
  98. that.onFileLoad(e.target.result)
  99. }
  100. })(this);
  101. if (!!window.FileReader) {
  102. reader.readAsBinaryString(o.shp);
  103. } else {
  104. this.onFileLoad(reader.readAsBinaryString(o.shp));
  105. }
  106. },
  107. onFileLoad: function(data) {
  108. this.stream = new Gordon.Stream(data)
  109. this.readFileHeader()
  110. this.readRecords()
  111. this.formatIntoGeoJson()
  112. if(this.options.dbf) this.dbf = IN_WORKER ?
  113. null :
  114. new DBF(this.options.dbf,function(data){
  115. that.addDBFDataToGeoJSON(data)
  116. that._postMessage()
  117. })
  118. else this._postMessage()
  119. },
  120. _postMessage: function() {
  121. var data = {
  122. header: this.header,
  123. records: this.records,
  124. dbf: this.dbf,
  125. geojson: this.geojson
  126. }
  127. if (IN_WORKER) postMessage(data)
  128. else if (this.callback) this.callback(data)
  129. },
  130. readFileHeader: function(){
  131. var s = this.stream,
  132. header = this.header = {}
  133. // The main file header is fixed at 100 bytes in length
  134. if(s < 100) throw "Invalid Header Length"
  135. // File code (always hex value 0x0000270a)
  136. header.fileCode = s.readSI32(true)
  137. if(header.fileCode != parseInt(0x0000270a))
  138. throw "Invalid File Code"
  139. // Unused; five uint32
  140. s.offset += 4 * 5
  141. // File length (in 16-bit words, including the header)
  142. header.fileLength = s.readSI32(true) * 2
  143. header.version = s.readSI32()
  144. header.shapeType = SHAPE_TYPES[s.readSI32()]
  145. // Minimum bounding rectangle (MBR) of all shapes contained within the shapefile; four doubles in the following order: min X, min Y, max X, max Y
  146. this._readBounds(header)
  147. // Z axis range
  148. header.rangeZ = {
  149. min: s.readDouble(),
  150. max: s.readDouble()
  151. }
  152. // User defined measurement range
  153. header.rangeM = {
  154. min: s.readDouble(),
  155. max: s.readDouble()
  156. }
  157. },
  158. readRecords: function(){
  159. var s = this.stream,
  160. records = this.records = [],
  161. record
  162. do {
  163. record = {}
  164. // Record number (1-based)
  165. record.id = s.readSI32(true)
  166. if(record.id == 0) break //no more records
  167. // Record length (in 16-bit words)
  168. record.length = s.readSI32(true) * 2
  169. record.shapeType = SHAPE_TYPES[s.readSI32()]
  170. // Read specific shape
  171. this["_read" + record.shapeType](record);
  172. records.push(record);
  173. } while(true);
  174. },
  175. _readBounds: function(object){
  176. var s = this.stream
  177. object.bounds = {
  178. left: s.readDouble(),
  179. bottom: s.readDouble(),
  180. right: s.readDouble(),
  181. top: s.readDouble()
  182. }
  183. return object
  184. },
  185. _readParts: function(record){
  186. var s = this.stream,
  187. nparts,
  188. parts = []
  189. nparts = record.numParts = s.readSI32()
  190. // since number of points always proceeds number of parts, capture it now
  191. record.numPoints = s.readSI32()
  192. // parts array indicates at which index the next part starts at
  193. while(nparts--) parts.push(s.readSI32())
  194. record.parts = parts
  195. return record
  196. },
  197. _readPoint: function(record){
  198. var s = this.stream
  199. record.x = s.readDouble()
  200. record.y = s.readDouble()
  201. return record
  202. },
  203. _readPoints: function(record){
  204. var s = this.stream,
  205. points = [],
  206. npoints = record.numPoints || (record.numPoints = s.readSI32())
  207. while(npoints--)
  208. points.push({
  209. x: s.readDouble(),
  210. y: s.readDouble()
  211. })
  212. record.points = points
  213. return record
  214. },
  215. _readMultiPoint: function(record){
  216. var s = this.stream
  217. this._readBounds(record)
  218. this._readPoints(record)
  219. return record
  220. },
  221. _readPolygon: function(record){
  222. var s = this.stream
  223. this._readBounds(record)
  224. this._readParts(record)
  225. this._readPoints(record)
  226. return record
  227. },
  228. _readPolyLine: function(record){
  229. return this._readPolygon(record);
  230. },
  231. formatIntoGeoJson: function(){
  232. var bounds = this.header.bounds,
  233. records = this.records,
  234. features = [],
  235. feature, geometry, points, fbounds, gcoords, parts, point,
  236. geojson = {}
  237. geojson.type = "FeatureCollection"
  238. geojson.bbox = [
  239. bounds.left,
  240. bounds.bottom,
  241. bounds.right,
  242. bounds.top
  243. ]
  244. geojson.features = features
  245. for (var r = 0, record; record = records[r]; r++){
  246. feature = {}, fbounds = record.bounds, points = record.points, parts = record.parts
  247. feature.type = "Feature"
  248. if (record.shapeType !== 'Point') {
  249. feature.bbox = [
  250. fbounds.left,
  251. fbounds.bottom,
  252. fbounds.right,
  253. fbounds.top
  254. ]
  255. }
  256. geometry = feature.geometry = {}
  257. switch (record.shapeType) {
  258. case "Point":
  259. geometry.type = "Point"
  260. geometry.coordinates = [
  261. record.x,
  262. record.y ]
  263. break
  264. case "MultiPoint":
  265. case "PolyLine":
  266. geometry.type = (record.shapeType == "PolyLine" ? "LineString" : "MultiPoint")
  267. gcoords = geometry.coordinates = []
  268. for (var p = 0; p < points.length; p++){
  269. var point = points[p]
  270. gcoords.push([point.x,point.y])
  271. }
  272. break
  273. case "Polygon":
  274. geometry.type = "Polygon"
  275. gcoords = geometry.coordinates = []
  276. for (var pt = 0; pt < parts.length; pt++){
  277. var partIndex = parts[pt],
  278. part = [],
  279. point
  280. // partIndex 0 == main poly, partIndex > 0 == holes in poly
  281. for (var p = partIndex; p < (parts[pt+1] || points.length); p++){
  282. point = points[p]
  283. part.push([point.x,point.y])
  284. }
  285. gcoords.push(part)
  286. }
  287. break
  288. default:
  289. }
  290. features.push(feature)
  291. }
  292. this.geojson = geojson
  293. if(this._addDataAfterLoad) this.addDBFDataToGeoJSON(this._addDataAfterLoad);
  294. },
  295. addDBFDataToGeoJSON: function(dbfData){
  296. if(!this.geojson) return (this._addDataAfterLoad = dbfData)
  297. this.dbf = dbfData
  298. var features = this.geojson.features,
  299. len = features.length,
  300. records = dbfData.records
  301. while(len--) features[len].properties = records[len]
  302. }
  303. }
  304. window["Shapefile"] = Shapefile;
  305. })(self);