-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathshp-reader.js
More file actions
257 lines (205 loc) · 7.49 KB
/
shp-reader.js
File metadata and controls
257 lines (205 loc) · 7.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
(function() {
var root = this;
var $ = root.jQuery;
/**
* Handles reading data from a shapefile
*/
ShpJS.ShpReader = function(options) {
this.initialize.call(this,arguments);
};
$.extend(ShpJS.ShpReader.prototype, {
_zipFile: null,
_shpBuffer: null,
_shpData: null,
_shxReader: null,
_dbfReader: null,
_wkid: null,
_features: null,
onparsecomplete: null,
shapeTypeCode: -1,
initialize: function() {
},
/**
* Reads features from the supplied file (zip file) and
* returns a FeatureSet containing the read features.
*/
read: function(file) {
var scope = this;
var reader = new FileReader();
// Called when the zip file finishes loading
reader.onload = function(e) {
// Convert the loaded array buffer to a string so it can be read by ZipFile
var buffer = e.target.result;
var arr = new Uint8Array(buffer);
var sArr = '';
for (var i=0; i<buffer.byteLength; i++)
sArr += String.fromCharCode(arr[i]);
// Create the ZipFile instance
scope._zipFile = new ZipFile(sArr);
// Find indices of shp/shx/dbf files
var shpIdx = -1,
shxIdx = -1,
dbfIdx = -1,
prjIdx = -1;
for (var idx=0; idx<scope._zipFile.filelist.length; idx++) {
var fname = scope._zipFile.filelist[idx].filename;
if (fname.match(/\.shp$/))
shpIdx = idx;
else if (fname.match(/\.shx$/))
shxIdx = idx;
else if (fname.match(/\.dbf$/))
dbfIdx = idx;
else if (fname.match(/\.prj$/))
prjIdx = idx;
}
if (shpIdx === -1 || shxIdx === -1 || dbfIdx === -1 || prjIdx === -1) {
console.error('Could not find one or more required files');
return;
}
// Get the file references
var fShp = scope._zipFile.filelist[shpIdx];
var fShx = scope._zipFile.filelist[shxIdx];
var fDbf = scope._zipFile.filelist[dbfIdx];
var fPrj = scope._zipFile.filelist[prjIdx];
// Convert the string into an array buffer
var content = scope._zipFile.extract(fShp.filename);
scope._shpBuffer = new ArrayBuffer(content.length);
scope._shpData = new DataView(scope._shpBuffer);
for (var i=0; i<content.length; i++)
scope._shpData.setUint8(i, content.charCodeAt(i));
// Create the SHX reader, required to parse the SHP
scope._shxReader = new ShpJS.ShxReader();
scope._shxReader.read.call(scope._shxReader, scope._zipFile.extract(fShx.filename));
// Create the DBF reader, and parse the DBF
scope._dbfReader = new ShpJS.DbfReader();
scope._dbfReader.read.call(scope._dbfReader, scope._zipFile.extract(fDbf.filename));
// Create the PRJ reader, and parse the PRJ
scope._wkid = ShpJS.PrjReader.prjToWkid(scope._zipFile.extract(fPrj.filename));
console.debug('WKID: ', scope._wkid);
// Parse the SHP
scope._parse.call(scope);
// Call the event handler, if one is present
if (scope.onparsecomplete || typeof scope.onparsecomplete == 'function') {
scope.onparsecomplete.call(scope, scope._features);
}
};
reader.readAsArrayBuffer(file);
},
_parse: function() {
this._features = new esri.tasks.FeatureSet();
// Read and set the shapetype code
this.shapeTypeCode = this._shpData.getInt32(32, true);
if (this.shapeTypeCode == ShpJS.Constants.POINT_SHAPE_TYPE)
this._features.geometryType = 'esriGeometryPoint';
else if (this.shapeTypeCode == ShpJS.Constants.POLYLINE_SHAPE_TYPE)
this._features.geometryType = 'esriGeometryPolyline';
else if (this.shapeTypeCode == ShpJS.Constants.POLYGON_SHAPE_TYPE)
this._features.geometryType = 'esriGeometryPolygon';
else if (this.shapeTypeCode == ShpJS.Constants.MULTIPOINT_SHAPE_TYPE)
this._features.geometryType = 'esriGeometryMultipoint';
for (var i=0; i<this._shxReader.featureIndices.length; i++) {
var fidx = this._shxReader.featureIndices[i].offset;
var flen = this._shxReader.featureIndices[i].length;
// Sanity check - make sure feature type is correct
if (this._shpData.getInt32(fidx+8, true) != this.shapeTypeCode) {
// TODO: Throw error here
console.error('Shape type mismatch, aborting parse!');
this._features = null;
return;
}
// Advance the offset to skip record #, content length, and shape type
fidx += 12;
var g;
if (this.shapeTypeCode == ShpJS.Constants.POINT_SHAPE_TYPE)
g = this._parsePoint(fidx);
else if (this.shapeTypeCode == ShpJS.Constants.MULTIPOINT_SHAPE_TYPE)
g = this._parseMultipoint(fidx);
else if (this.shapeTypeCode == ShpJS.Constants.POLYLINE_SHAPE_TYPE)
g = this._parsePolyline(fidx);
else if (this.shapeTypeCode == ShpJS.Constants.POLYGON_SHAPE_TYPE)
g = this._parsePolygon(fidx);
// Get the attributes from DBF
var attrs = this._dbfReader.getAttributes(i);
// Create the Graphics object and push into features array
var gfx = new esri.Graphic(g, null, attrs, null);
this._features.features.push(gfx);
}
console.debug('features parsed: ', this._features);
},
_parsePoint: function(idx) {
return new esri.geometry.Point(
this._shpData.getFloat64(idx, true),
this._shpData.getFloat64(idx+8, true),
this._wkid
);
},
_parseMultipoint: function(idx) {
var nump = this._shpData.getInt32(idx+32, true);
var mp = new esri.geometry.Multipoint(this._wkid);
idx+=36;
for (var i=0; i<nump; i++) {
mp.addPoint([this._shpData.getFloat64(idx, true), this._shpData.getFloat64(idx+8, true)]);
idx += 16;
}
return mp;
},
_parsePolyline: function(idx) {
var numpaths = this._shpData.getInt32(idx+32, true);
var numpoints = this._shpData.getInt32(idx+36, true);
idx += 40;
var pathIdxs = [];
for (var i=0; i<numpaths; i++) {
pathIdxs.push(this._shpData.getInt32(idx, true));
idx += 4;
}
var pline = new esri.geometry.Polyline(this._wkid);
// Read the points for each individual part
for (var i=0; i<numpaths; i++) {
// Current part index
var pidx = pathIdxs[i];
// Loop through the points starting at the current part index and
// either ending at the next part index, or at numpoints which is the
// last point index. Add all the read points to the path
var path = [];
for (var j=pidx; j< (i < numpaths-1 ? pathIdxs[i+1] : numpoints); j++) {
path.push([
this._shpData.getFloat64(idx, true),
this._shpData.getFloat64(idx+8, true)
]);
idx += 16;
}
pline.addPath(path);
}
return pline;
},
_parsePolygon: function(idx) {
var numrings = this._shpData.getInt32(idx+32, true);
var numpoints = this._shpData.getInt32(idx+36, true);
idx += 40;
var ringIdxs = [];
for (var i=0; i<numrings; i++) {
ringIdxs.push(this._shpData.getInt32(idx, true));
idx += 4;
}
var pgon = new esri.geometry.Polygon(this._wkid);
// Read the points for each individual part
for (var i=0; i<numrings; i++) {
// Current part index
var pidx = ringIdxs[i];
// Loop through the points starting at the current part index and
// either ending at the next part index, or at numpoints which is the
// last point index. Add all the read points to the ring
var ring = [];
for (var j=pidx; j< (i < numrings-1 ? ringIdxs[i+1] : numpoints); j++) {
ring.push([
this._shpData.getFloat64(idx, true),
this._shpData.getFloat64(idx+8, true)
]);
idx += 16;
}
pgon.addRing(ring);
}
return pgon;
}
});
}).call(this);