#include #include #include #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; irecords; 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; ifieldc; 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; ipartc; 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; ipointv[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; ifieldc;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; ifieldc; 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; ifieldc; 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; ipointc; 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; ifieldc; 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 ""; } }