#include <u.h>
#include <libc.h>
#include <bio.h>
#include "shp.h"
#include "fn.h"
/* FIXME: this belongs elsewhere */
#define max(a,b) ((a)>(b)?(a):(b))
static int
_shp_open_shp_header(shp_handle *shp){
uchar buf[100];
Bread(shp->pfile, buf, 100);
shp->size = bgetl(buf+24);
Bread(shp->xfile, buf, 100);
if(bgetl(buf)!=0x270a) return 0;
shp->records = (bgetl(buf+24)-50)/4;
shp->type = buf[32];
shp->«x = lgetd(buf+36);
shp->«y = lgetd(buf+44);
shp->»x = lgetd(buf+52);
shp->»y = lgetd(buf+60);
shp->«z = lgetd(buf+68);
shp->»z = lgetd(buf+76);
shp->«m = lgetd(buf+84);
shp->»m = lgetd(buf+92);
return 1;
}
static int
_shp_open_shp_records(shp_handle *shp){
uchar buf[8];
int i;
shp->rec_offset = malloc(sizeof(*shp->rec_offset) * max(1,shp->records));
shp->rec_size = malloc(sizeof(*shp->rec_size) * max(1,shp->records));
if(!shp->rec_offset||!shp->rec_size) return 0;
for(i=0; i<shp->records; i++){
if(Bread(shp->xfile, buf, 8) != 8){
shp->records=i;
realloc(shp->rec_offset, sizeof(*shp->rec_offset)*i);
realloc(shp->rec_size, sizeof(*shp->rec_size)*i);
break;
}
shp->rec_offset[i]=bgetl(buf)*2;
shp->rec_size[i]=bgetl(buf+4)*2;
}
return 1;
}
static int
_shp_open_dbf_header(shp_handle *shp){
uchar buf[32];
if(Bread(shp->dfile, buf, 32) != 32) return 0;
shp->headerw = lgets(buf+8);
shp->fieldc = shp->headerw/32 - 1;
shp->recordc = lgetl(buf+4);
shp->recordw = lgets(buf+10);
return 1;
}
static int
_shp_open_dbf_attrs(shp_handle *shp){
uchar buf[32];
int i;
shp->fieldv = malloc(shp->fieldc*sizeof(*shp->fieldv));
if(!shp->fieldv) return 0;
for(i=0; i<shp->fieldc; i++){
Bread(shp->dfile, buf, 32);
memcpy(shp->fieldv[i].name, buf, 11); /* are these 10 chars always nul-terminated? */
shp->fieldv[i].offset = (i==0)?1:shp->fieldv[i-1].offset + shp->fieldv[i-1].size;
shp->fieldv[i].size = buf[16];
shp->fieldv[i].decimals = buf[17];
shp->fieldv[i].type = buf[11];
}
return 1;
}
shp_handle *
shp_open(char *name, int mode) {
shp_handle *shp;
char *file;
int r;
shp=malloc(sizeof(shp_handle));
file=malloc(strlen(name)+5);
if(!shp||!file) {
free(shp);
free(file);
return nil;
}
strcpy(file, name);
strcat(file, ".shp");
shp->pfile = Bopen(file,mode);
strcpy(file, name);
strcat(file, ".shx");
shp->xfile = Bopen(file,mode);
strcpy(file, name);
strcat(file, ".dbf");
shp->dfile = Bopen(file,mode);
free(file);
/* should no dfile be treated as no attributes? */
if(!shp->pfile||!shp->xfile||!shp->dfile) {
shp_close(shp);
werrstr("shp_open unable to open %s.sh[px]: %r\n", name);
return nil;
}
r=
_shp_open_shp_header(shp) &&
_shp_open_shp_records(shp) &&
_shp_open_dbf_header(shp) &&
_shp_open_dbf_attrs(shp);
if(!r){
shp_close(shp);
return nil;
}
if(mode==OREAD) {
Bterm(shp->xfile);
shp->xfile=nil;
}
return shp;
}
void
shp_close(shp_handle *shp){
if(!shp) return;
/* if writing is ever implemented put stuff here */
if(shp->pfile) Bterm(shp->pfile);
if(shp->xfile) Bterm(shp->xfile);
if(shp->dfile) Bterm(shp->dfile);
free(shp->rec_offset);
free(shp->rec_size);
free(shp->fieldv);
free(shp);
}
static void
obj_box(shp_object *obj, Biobuf *in) {
uchar buf[32];
Bread(in, buf, 32);
obj->«x = lgetd(buf);
obj->«y = lgetd(buf+8);
obj->»x = lgetd(buf+16);
obj->»y = lgetd(buf+24);
}
static void
obj_parts(shp_object *obj, Biobuf *in) {
uchar buf[4];
int i;
obj->partv=malloc(obj->partc*sizeof(*obj->partv));
if(!obj->partv) return;
for(i=0; i<obj->partc; i++) {
Bread(in, buf, 4);
obj->partv[i] = lgetl(buf);
}
}
static void
obj_points(shp_object *obj, Biobuf *in) {
uchar buf[8];
int i, floats;
floats=obj->pointc*obj->pointw;
obj->pointv=malloc(floats*sizeof(*obj->pointv));
if(!obj->pointv) return;
for(i=0; i<floats; i++) {
Bread(in, buf, 8);
obj->pointv[i] = lgetd(buf);
}
}
static int
rfirstspace(char *str, int offset){
int i;
for(i=offset-1; i!=0; i--)
if(str[i] != ' ') break;
return i+1;
}
int
shp_read(shp_handle *shp, shp_object *obj, int num){
uchar buf[256];
int i, len;
if(!shp||!obj||num<0||num>=shp->records) return 0;
free(obj->partv);
free(obj->pointv);
Bseek(shp->pfile, shp->rec_offset[num], 0);
Bread(shp->pfile, buf, 12);
obj->type = lgetl(buf+8);
switch(obj->type) {
case Shp_point:
Bread(shp->pfile, buf, 16);
obj->«x = lgetd(buf);
obj->«y = lgetd(buf+8);
break;
case Shp_pointm:
Bread(shp->pfile, buf, 24);
obj->«x = lgetd(buf);
obj->«y = lgetd(buf+8);
obj->«m = lgetd(buf+16);
break;
case Shp_pointz:
Bread(shp->pfile, buf, 32);
obj->«x = lgetd(buf);
obj->«y = lgetd(buf+8);
obj->«z = lgetd(buf+16);
obj->«m = lgetd(buf+24);
break;
case Shp_polyline:
case Shp_polygon:
obj_box(obj, shp->pfile);
Bread(shp->pfile, buf, 8);
obj->partc = lgetl(buf);
obj->pointw = 2;
obj->pointc = lgetl(buf+4);
obj_parts(obj, shp->pfile);
obj_points(obj, shp->pfile);
break;
case Shp_null:
default:
break;
}
Bseek(shp->dfile, shp->recordw*num+shp->headerw+1, 0);
for(i=0; i<shp->fieldc;i++){
Bread(shp->dfile, buf, shp->fieldv[i].size);
switch(shp->fieldv[i].type){
case 'C':
case 'D': /* date YYYYMMDD */
case 'N': /* ascii float */
case 'F': /* long ascii float */
memcpy(obj->attrv[i], buf, shp->fieldv[i].size);
len = rfirstspace(obj->attrv[i], shp->fieldv[i].size);
((char *)obj->attrv[i])[len] = '\0';
break;
case 'I':
*(int *)obj->attrv[i] = lgetl(buf);
break;
case 'L': /* boolean [YyTt]->true [NnFf]->false [? ]->undefined */
case 'O': /* binary double */
default:
break;
}
}
return 1;
}
static char *shp_types[31] = {
"Null", "Point", nil, "Polyline", nil, "Polygon", nil, nil, "Multipoint", nil,
nil, "PointZ", nil, "PolylineZ", nil, "PolygonZ", nil, nil, "MultipointZ", nil,
nil, "PointM", nil, "PolylineM", nil, "PolygonZ", nil, nil, "MultipointZ", nil,
"Multipatch",
};
void
shp_dump(Biobuf *out, shp_handle *shp){
char *s, *pad;
int i;
s = shp->type<32?shp_types[shp->type]:nil;
Bprint(out, "type fields records xmin ymin xmax ymax\n");
Bprint(out, "%s %d %d %f %f %f %f\n", s, shp->fieldc, shp->records, shp->«x, shp->«y, shp->»x, shp->»y);
pad="";
for(i=0; i<shp->fieldc; i++){
Bprint(out, "%s%s", pad, shp->fieldv[i].name);
pad=" ";
}
switch(shp->type) {
case Shp_point:
Bprint(out, " x y");
break;
case Shp_polyline:
case Shp_polygon:
Bprint(out, " xmin ymin xmax ymax x... y...");
break;
}
Bputc(out, '\n');
}
void
obj_dump(Biobuf *out, shp_handle *shp, shp_object *obj){
int i;
char *pad;
pad="";
for(i=0; i<shp->fieldc; i++){
Bprint(out, pad);
Bprint(out, shp_attr_fmt(shp, i), obj->attrv[i]);
pad=" ";
}
switch(obj->type) {
case Shp_point:
Bprint(out, "%s%f %f", pad, obj->«x, obj->«y);
break;
case Shp_polyline:
case Shp_polygon:
Bprint(out, "%s%f %f %f %f", pad, obj->«x, obj->«y, obj->»x, obj->»y);
for(i=0; i<obj->pointc; i++){
Bprint(out, " %f", obj->pointv[i]);
}
break;
default:
Bprint(out, "Unsupported type\n");
break;
}
Bputc(out, '\n');
}
shp_object *
shp_alloc_object(shp_handle *shp){
shp_object *obj;
int i, sz;
if(!shp) return nil;
obj = malloc(sizeof(*obj));
if(!obj) return nil;
obj->attrv = malloc(shp->fieldc*sizeof(obj->attrv)+1);
if(!obj->attrv) {
free(obj);
return nil;
}
for(i=0; i<shp->fieldc; i++){ /* should probably test these... */
switch(shp->fieldv[i].type){
case 'I':
sz = 4;
break;
case 'C':
case 'D': /* date YYYYMMDD */
case 'N': /* ascii float */
case 'F': /* long ascii float */
default:
sz = shp->fieldv[i].size+1;
break;
}
obj->attrv[i] = malloc(sz);
}
obj->partv=nil;
obj->pointv=nil;
return obj;
}
char *
shp_attr_fmt(shp_handle *shp, int i){
switch(shp->fieldv[i].type){
case 'C':
case 'D':
case 'F':
return "%s";
default:
return "";
}
}
|