1
0
mirror of https://github.com/wavded/js-shapefile-to-geojson synced 2024-11-23 14:34:54 +00:00
js-shapefile-to-geojson/shapefile.js
Chad Burt 825ce8af25 Fixed support for Point datasets
While some support existed, an attempt was being
made to create an unnecessary(?) bounding box for
each point feature. Also swapped access of the x,y
coordinates from record.geometry.x|y to record.x|y.
2013-09-09 15:45:38 -07:00

327 lines
9.8 KiB
JavaScript

(function(window,undefined){
if(window.document && window.Worker){
var worker = new Worker("shapefile.js")
var Shapefile = function(o, callback){
var
w = this.worker = worker,
t = this,
o = typeof o == "string" ? {shp: o} : o
w.onmessage = function(e){
t.data = e.date
if(callback) callback(e.data)
}
w.postMessage(["Load", o])
if(o.dbf) this.dbf = new DBF(o.dbf,function(data){
w.postMessage(["Add DBF Attributes", data])
})
}
window["Shapefile"] = Shapefile
return
}
var IN_WORKER = !window.document
if (IN_WORKER) {
importScripts('stream.js')
onmessage = function(e){
switch (e.data[0]) {
case "Load":
window.shapefile = new Shapefile(e.data[1])
break
case "Add DBF Attributes":
window.shapefile.addDBFDataToGeoJSON(e.data[1])
window.shapefile._postMessage()
break
default:
}
};
}
var SHAPE_TYPES = {
"0": "Null Shape",
"1": "Point", // standard shapes
"3": "PolyLine",
"5": "Polygon",
"8": "MultiPoint",
"11": "PointZ", // 3d shapes
"13": "PolyLineZ",
"15": "PolygonZ",
"18": "MultiPointZ",
"21": "PointM", // user-defined measurement shapes
"23": "PolyLineM",
"25": "PolygonM",
"28": "MultiPointM",
"31": "MultiPatch"
}
var Shapefile = function(o,callback){
var xhr = new XMLHttpRequest(),
that = this,
o = typeof o == "string" ? {shp: o} : o
xhr.open("GET", o.shp, false)
xhr.overrideMimeType("text/plain; charset=x-user-defined")
xhr.send()
if(200 != xhr.status)
throw "Unable to load " + o.shp + " status: " + xhr.status
this.url = o.shp
this.stream = new Gordon.Stream(xhr.responseText)
this.callback = callback
this.readFileHeader()
this.readRecords()
this.formatIntoGeoJson()
if(o.dbf) this.dbf = IN_WORKER ?
null :
new DBF(o.dbf,function(data){
that.addDBFDataToGeoJSON(data)
that._postMessage()
})
else this._postMessage
}
Shapefile.prototype = {
constructor: Shapefile,
_postMessage: function() {
var data = {
header: this.header,
records: this.records,
dbf: this.dbf,
geojson: this.geojson
}
if (IN_WORKER) postMessage(data)
else if (this.callback) this.callback(data)
},
readFileHeader: function(){
var s = this.stream,
header = this.header = {}
// The main file header is fixed at 100 bytes in length
if(s < 100) throw "Invalid Header Length"
// File code (always hex value 0x0000270a)
header.fileCode = s.readSI32(true)
if(header.fileCode != parseInt(0x0000270a))
throw "Invalid File Code"
// Unused; five uint32
s.offset += 4 * 5
// File length (in 16-bit words, including the header)
header.fileLength = s.readSI32(true) * 2
header.version = s.readSI32()
header.shapeType = SHAPE_TYPES[s.readSI32()]
// 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
this._readBounds(header)
// Z axis range
header.rangeZ = {
min: s.readDouble(),
max: s.readDouble()
}
// User defined measurement range
header.rangeM = {
min: s.readDouble(),
max: s.readDouble()
}
},
readRecords: function(){
var s = this.stream,
records = this.records = [],
record
do {
record = {}
// Record number (1-based)
record.id = s.readSI32(true)
if(record.id == 0) break //no more records
// Record length (in 16-bit words)
record.length = s.readSI32(true) * 2
record.shapeType = SHAPE_TYPES[s.readSI32()]
// Read specific shape
this["_read" + record.shapeType](record);
records.push(record);
} while(true);
},
_readBounds: function(object){
var s = this.stream
object.bounds = {
left: s.readDouble(),
bottom: s.readDouble(),
right: s.readDouble(),
top: s.readDouble()
}
return object
},
_readParts: function(record){
var s = this.stream,
nparts,
parts = []
nparts = record.numParts = s.readSI32()
// since number of points always proceeds number of parts, capture it now
record.numPoints = s.readSI32()
// parts array indicates at which index the next part starts at
while(nparts--) parts.push(s.readSI32())
record.parts = parts
return record
},
_readPoint: function(record){
var s = this.stream
record.x = s.readDouble()
record.y = s.readDouble()
return record
},
_readPoints: function(record){
var s = this.stream,
points = [],
npoints = record.numPoints || (record.numPoints = s.readSI32())
while(npoints--)
points.push({
x: s.readDouble(),
y: s.readDouble()
})
record.points = points
return record
},
_readMultiPoint: function(record){
var s = this.stream
this._readBounds(record)
this._readPoints(record)
return record
},
_readPolygon: function(record){
var s = this.stream
this._readBounds(record)
this._readParts(record)
this._readPoints(record)
return record
},
_readPolyLine: function(record){
return this._readPolygon(record);
},
formatIntoGeoJson: function(){
var bounds = this.header.bounds,
records = this.records,
features = [],
feature, geometry, points, fbounds, gcoords, parts, point,
geojson = {}
geojson.type = "FeatureCollection"
geojson.bbox = [
bounds.left,
bounds.bottom,
bounds.right,
bounds.top
]
geojson.features = features
for (var r = 0, record; record = records[r]; r++){
feature = {}, fbounds = record.bounds, points = record.points, parts = record.parts
feature.type = "Feature"
if (record.shapeType !== 'Point') {
feature.bbox = [
fbounds.left,
fbounds.bottom,
fbounds.right,
fbounds.top
]
}
geometry = feature.geometry = {}
switch (record.shapeType) {
case "Point":
geometry.type = "Point"
geometry.coordinates = [
record.x,
record.y ]
break
case "MultiPoint":
case "PolyLine":
geometry.type = (record.shapeType == "PolyLine" ? "LineString" : "MultiPoint")
gcoords = geometry.coordinates = []
for (var p = 0; p < points.length; p++){
var point = points[p]
gcoords.push([point.x,point.y])
}
break
case "Polygon":
geometry.type = "Polygon"
gcoords = geometry.coordinates = []
for (var pt = 0; pt < parts.length; pt++){
var partIndex = parts[pt],
part = [],
point
// partIndex 0 == main poly, partIndex > 0 == holes in poly
for (var p = partIndex; p < (parts[pt+1] || points.length); p++){
point = points[p]
part.push([point.x,point.y])
}
gcoords.push(part)
}
break
default:
}
features.push(feature)
}
this.geojson = geojson
if(this._addDataAfterLoad) this.addDBFDataToGeoJSON(this._addDataAfterLoad);
},
addDBFDataToGeoJSON: function(dbfData){
if(!this.geojson) return (this._addDataAfterLoad = dbfData)
this.dbf = dbfData
var features = this.geojson.features,
len = features.length,
records = dbfData.records
while(len--) features[len].properties = records[len]
}
}
window["Shapefile"] = Shapefile;
})(self)