Plan 9 from Bell Labs’s /usr/web/sources/contrib/tristan/root/sys/src/cmd/geo/shp/shp.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#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 "";
	}
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.