Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

adding in shape file parser sample

  • Loading branch information...
mmarks
mmarks committed Dec 14, 2012
1 parent f300478 commit b36196029d4b79f1f01586c5737e7cd513b590ba
@@ -0,0 +1,134 @@
/**
* @fileoverview Parses a .dbf file based on the xbase standards as documented
* here: http://www.clicketyclick.dk/databases/xbase/format/dbf.html
* @author Mano Marks
*/

// Creates global namespace.
DBF = {};

DBFParser = function() {};

/**
* Executes a binary XHR to load a .dbf file and then creates a callback to
* handle the result.
* @param {string} url URL to the .dbf file.
* @param {function(Object)} callback the function to be called when finished.
* @param {Function} onerror the function to be called in case of an error
* loading the file.
*/
DBFParser.load = function(url, callback, onerror) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
var d = new DBFParser().parse(xhr.response);
callback(d);
};
xhr.onerror = onerror;
xhr.open('GET', url);
xhr.send(null);
};

/**
* Parses through the .dbf file byte by byte
* @param {arraybuffer} arrayBuffer the ArrayBuffer created by loading the file
* in XHR.
* @return {object} o An object representing the .dbf file.
*/
DBFParser.prototype.parse = function(arrayBuffer) {
var o = {};
var dv = new DataView(arrayBuffer);
var idx = 0;
o.version = dv.getInt8(idx, false);

idx += 1;
o.year = dv.getUint8(idx) + 1900;
idx += 1;
o.month = dv.getUint8(idx);
idx += 1;
o.day = dv.getUint8(idx);
idx += 1;

o.numberOfRecords = dv.getInt32(idx, true);
idx += 4;
o.bytesInHeader = dv.getInt16(idx, true);
idx += 2;
o.bytesInRecord = dv.getInt16(idx, true);
idx += 2;
//reserved bytes
idx += 2;
o.incompleteTransation = dv.getUint8(idx);
idx += 1;
o.encryptionFlag = dv.getUint8(idx);
idx += 1;
// skip free record thread for LAN only
idx += 4;
// reserved for multi-user dBASE in dBASE III+
idx += 8;
o.mdxFlag = dv.getUint8(idx);
idx += 1;
o.languageDriverId = dv.getUint8(idx);
idx += 1;
// reserved bytes
idx += 2;

o.fields = [];
while (true) {
var field = {};
var nameArray = [];
for (var i = 0; i < 10; i++) {
var letter = dv.getUint8(idx);
if (letter != 0) nameArray.push(String.fromCharCode(letter));
idx += 1;
}
field.name = nameArray.join('');
idx += 1;
field.type = String.fromCharCode(dv.getUint8(idx));
idx += 1;
// Skip field data address
idx += 4;
field.fieldLength = dv.getUint8(idx);
idx += 1;
//field.decimalCount = dv.getUint8(idx);
idx += 1;
// Skip reserved bytes multi-user dBASE.
idx += 2;
field.workAreaId = dv.getUint8(idx);
idx += 1;
// Skip reserved bytes multi-user dBASE.
idx += 2;
field.setFieldFlag = dv.getUint8(idx);
idx += 1;
// Skip reserved bytes.
idx += 7;
field.indexFieldFlag = dv.getUint8(idx);
idx += 1;
o.fields.push(field);
var test = dv.getUint8(idx);
// Checks for end of field descriptor array. Valid .dbf files will have this
// flag.
if (dv.getUint8(idx) == 0x0D) break;
}

idx += 1;
o.records = [];

for (var i = 0; i < o.numberOfRecords; i++) {
var record = {};
// Skip record deleted flag.
//record["recordDeleted"] = String.fromCharCode(dv.getUint8(idx));
idx += 1;
for (var j = 0; j < o.fields.length; j++) {
var charString = [];
for (var h = 0; h < o.fields[j].fieldLength; h++) {
charString.push(String.fromCharCode(dv.getUint8(idx)));
idx += 1;
}
record[o.fields[j].name] = charString.join('').trim();

}
o.records.push(record);
}

return o;
};
@@ -0,0 +1,16 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="style.css">
<script src="lib/dbf.js"></script>
<script src="https://raw.github.com/kig/shp.js/master/shp.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?sensor=false">
</script>
<script src="shploadsample.js"></script>
</head>
<body>
<div id="map"></div><div id="side"></div>
</body>
</html>
@@ -0,0 +1,154 @@
/*
* @fileoverview Demonstrates how to load a shapefile and parse it using
* JavaScript.
* @author Mano Marks
*/

var map;
var dbf;
var shp;
var infowindow;

// Creates the map, loads in the SHP and DBF files.
function init() {
map = new google.maps.Map(document.getElementById('map'), {
zoom: 4,
center: new google.maps.LatLng(39.3, -95.8),
mapTypeId: google.maps.MapTypeId.ROADMAP,
styles: [
{
featureType: 'water',
stylers: [{ color: '#c3cfdd'}]
},
{
featureType: 'poi',
stylers: [{visibility: 'off'}]
}
]
});

SHPParser.load('tl_2010_06_tract10/tl_2010_06_tract10.shp', shpLoad,
shpLoadError);
DBFParser.load('tl_2010_06_tract10/tl_2010_06_tract10.dbf', dbfLoad,
dbfLoadError);
}

// Handles the callback from loading DBFParser by assigning the dbf to a global.
function dbfLoad(db) {
dbf = db;
if (dbf && shp) {
render();
}
}

/* Handles the callback from loading the shp file.
* @param {SHPParser} shp The results of parsing the shp file.
*/
function shpLoad(sh) {
shp = sh;
if (dbf && shp) {
render();
}
}

/**
* Takes the geometries from the shp file and the data from the dbf file by
* creating a polygon for each shp record with an InfoWindow and right panel
* for the data from the dbf file.
*/
function render() {
var points;
var type;
var ne = new google.maps.LatLng(shp.maxY, shp.maxX);
var sw = new google.maps.LatLng(shp.minY, shp.minX);
var bounds = new google.maps.LatLngBounds(sw, ne);
map.fitBounds(bounds);
for (var i = 0; i < shp.records.length; i++) {
var shape = shp.records[i].shape;
switch (shape.type) {
case 1:
var marker = new google.maps.Marker({
position: new google.maps.LatLng(shape.content.y,shape.content.x),
map: map
});
break;

case 3:
points = pathToMVCArray(shape.content.points);
break;

case 5:

// split into rings
var polygonPoints = new google.maps.MVCArray();
var parts = shape.content.parts;
if (parts.length === 1) {
polygonPoints.push(pathToMVCArray(shape.content.points));
} else {
var j;
for (j = 0; j < parts.length - 1; j++) {
polygonPoints.push(pathToMVCArray(shape.content.points.subarray(
2 * parts[j], 2 * parts[j + 1])));
if (2 * parts[j + 1] > shape.content.points.length) {
throw new Error('part index beyond points array end');
}
}
}
// create a polygon.
var polygon = new google.maps.Polygon({
strokeWeight: .3,
fillOpacity: .2,
paths: polygonPoints,
map: map
});
polygon.tractId = i;

// Create InfoWindow at click point and put data in side panel.
google.maps.event.addListener(polygon, 'click', function(e) {
if (typeof infowindow != 'undefined') {
infowindow.close();
}
var htmlContent = recordHtmlContent(dbf.records[this.tractId]);
infowindow = new google.maps.InfoWindow({
content: htmlContent,
position: e.latLng,
map: map
});
document.getElementById('side').innerHTML = htmlContent;
});
}
}
}

/* Create a nice presentation for the attribute data.
* @param {object} record An object representing the individual record.
*/
function recordHtmlContent(record) {
var content = '';
for (var key in record) {
content += '<b>' + key + '</b>: ' + record[key] + '<br>';
}
return content;
}

/* Create an MVCArray out of a set of points ordered longitude/latitude
* @param {array} path an array of points.
*/
function pathToMVCArray(path) {
var polygonPoints = new google.maps.MVCArray();
for (var i = 0; i < path.length; i += 2) {
polygonPoints.push(new google.maps.LatLng(path[i + 1], path[i]));
}
return polygonPoints;
}

// error handler for shploader.
function shpLoadError() {
window.console.log('shp file failed to load');
}

// error handler for dbfloader.
function dbfLoadError() {
console.log('dbf file failed to load');
}
document.addEventListener('DOMContentLoaded', init, false);
@@ -0,0 +1,12 @@
#map {
margin: 0;
padding: 0;
height: 600px;
width: 800px;
float: left;
}
#side {
width: 300px;
height: 600px;
float: right;
}
Binary file not shown.
@@ -0,0 +1 @@
GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
Binary file not shown.
Oops, something went wrong.

0 comments on commit b361960

Please sign in to comment.
You can’t perform that action at this time.