js-shapefile-to-geojson/dbf.js

250 lines
7.5 KiB
JavaScript

(function(window,undefined){
if(window.document && window.Worker){
var worker = new Worker("dbf.js")
var DBF = function(url, callback){
var
w = this._worker = worker,
t = this
w.onmessage = function(e){
t.data = e.data
if (callback) callback(e.data);
};
w.postMessage(url)
}
window["DBF"] = DBF
return
}
var IN_WORKER = !window.document
if (IN_WORKER) {
importScripts('stream.js')
onmessage = function(e){
new DBF(e.data);
};
}
var
DBASE_LEVEL = {
"3": "dBASE Level 5",
"4": "dBase Level 7"
},
DBASE_FIELD_TYPE = {
"N": "Number",
"C": "Character", // binary
"L": "Logical",
"D": "Date",
"M": "Memo", // binary
"F": "Floating point",
"B": "Binary",
"G": "General",
"P": "Picture",
"Y": "Currency",
"T": "DateTime",
"I": "Integer",
"V": "VariField",
"X": "Variant",
"@": "Timestamp",
"O": "Double",
"+": "Autoincrement", // (dBase Level 7)
"O": "Double", // (dBase Level 7)
"@": "Timestamp" // (dBase Level 7)
}
var DBF = function(url, callback){
if (!!(url.lastModifiedDate || url.lastModified))
this.handleFile(url, callback);
else
this.handleUri(url, callback);
}
DBF.prototype = {
constructor: DBF,
handleFile: function(file, callback) {
this.callback = callback;
if (!!window.FileReader) {
var reader = new FileReader();
} else {
var reader = new FileReaderSync();
}
reader.onload = (function(that){
return function(e){
that.onFileLoad(e.target.result)
}
})(this);
if (!!window.FileReader) {
reader.readAsBinaryString(file);
} else {
this.onFileLoad(reader.readAsBinaryString(file));
}
},
onFileLoad: function(data) {
this.stream = new Gordon.Stream(data)
this.readFileHeader()
this.readFieldDescriptions()
this.readRecords()
this._postMessage()
},
handleUri: function(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false)
xhr.overrideMimeType("text/plain; charset=x-user-defined")
xhr.send()
if(200 != xhr.status)
throw "Unable to load " + url + " status: " + xhr.status
this.stream = new Gordon.Stream(xhr.responseText)
this.callback = callback
this.readFileHeader()
this.readFieldDescriptions()
this.readRecords()
this._postMessage()
},
_postMessage: function() {
var data = {
header: this.header,
fields: this.fields,
records: this.records
}
if (IN_WORKER) postMessage(data)
else if (this.callback) this.callback(data)
},
readFileHeader: function(){
var s = this.stream,
header = this.header = {},
date = new Date;
header.version = DBASE_LEVEL[s.readSI8()]
// Date of last update; in YYMMDD format. Each byte contains the number as a binary. YY is added to a base of 1900 decimal to determine the actual year. Therefore, YY has possible values from 0x00-0xFF, which allows for a range from 1900-2155.
date.setUTCFullYear(1900 + s.readSI8())
date.setUTCMonth(s.readSI8())
date.setUTCDate(s.readSI8())
header.lastUpdated = date
// Number of records in file
header.numRecords = s.readSI32()
// Position of first data record
header.firstRecordPosition = s.readSI16()
// Length of one data record, including delete flag
header.recordLength = s.readSI16()
// Reserved; filled with zeros
s.offset += 16
/*
Table flags:
0x01 file has a structural .cdx
0x02 file has a Memo field
0x04 file is a database (.dbc)
This byte can contain the sum of any of the above values. For example, the value 0x03 indicates the table has a structural .cdx and a Memo field.
*/
header.flags = s.readSI8()
// Code page mark
header.codePageMark = s.readSI8()
// Reserved; filled with zeros.
s.offset += 2
},
readFieldDescriptions: function(){
var s = this.stream,
fields = [],
field
while (s.readSI8() != 0x0D) {
s.offset--
field = {}
// Field name with a maximum of 10 characters. If less than 10, it is padded with null characters (0x00).
field.name = s.readString(11).replace(/\u0000/g,"")
field.type = DBASE_FIELD_TYPE[s.readString(1)]
// Displacement of field in record
field.fieldDisplacement = s.readSI32()
// Length of field (in bytes)
field.fieldLength = s.readUI8()
// Number of decimal places
field.decimals = s.readSI8()
/*
Field flags:
0x01 System Column (not visible to user)
0x02 Column can store null values
0x04 Binary column (for CHAR and MEMO only)
0x06 (0x02+0x04) When a field is NULL and binary (Integer, Currency, and Character/Memo fields)
0x0C Column is autoincrementing
*/
field.flags = s.readSI8()
// Value of autoincrement Next value
field.autoincrementNextValue = s.readSI32()
// Value of autoincrement Step value
field.autoincrementStepValue = s.readSI8()
// Reserved
s.offset += 8
fields.push(field)
}
this.fields = fields
},
readRecords: function(){
var s = this.stream,
numRecords = this.header.numRecords,
recordsOffset = this.header.firstRecordPosition,
recordSize = this.header.recordLength,
fields = this.fields,
numFields = fields.length,
records = [],
field, record
for (var index = 0; index < numRecords; index++) {
s.offset = recordsOffset + index * recordSize
record = {}
// Data records begin with a delete flag byte. If this byte is an ASCII space (0x20), the record is not deleted. If the first byte is an asterisk (0x2A), the record is deleted
record._isDeleted = s.readSI8() == 42
for(var i = 0; i < numFields; i++){
field = fields[i]
record[field.name] = s.readString(field.fieldLength).trim();
}
records.push(record);
}
this.records = records
}
}
window["DBF"] = DBF;
})(self);