User:Dbenbenn/procshape.c

Here is a simple C program I use for converting Shapefile output to SVG format. Specifically, after using shpdump to decode the Shapefile, and proj to project the data, I use this program to select the shapes I want and make the output SVG-ready.

Suppose you have projected data in data, a list of names for the shapes in names, and a list of shape numbers you want (1-based) in numbers. Use

procshape data numbers names > foo.svg

To compile this program, use

gcc -lm -Wall procshape.c -o procshape

Here's the code:

#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdbool.h>

/*
Usage: procshape shapes numbers [names]
shapes is the file that shpdump outputs.  numbers is the shape numbers to select.  names is the name of each shape.
*/

const int dig = 100;

enum {
    NONE, POLYGON, LINE, POINT
} shapetype = NONE;

FILE *shapes;
char *shapesline = NULL;
size_t shapeslinesize = 0;
long int shapenum = 1;

FILE *numbers = NULL;
char *numbersline = NULL;
size_t numberslinesize = 0;

FILE *names = NULL;
char *namesline = NULL;
size_t nameslinesize = 0;

int minx = 0, miny = 0, maxx = 0, maxy = 0;

int dropped = 0;

int sign(int x) {
    if (x >= 0)
	return 1;
    return -1;
}

void skip_lines(FILE *foo, long int lines)
{
    char *line = NULL;
    size_t n = 0;

    while (lines > 0) {
	if (-1 == getline(&line, &n, foo))
	    break;
	lines--;
    }
    free(line);
}

void skip_shapes(FILE *foo, long int shapes)
{
    if (shapetype == POINT)
	skip_lines(foo, shapes);
    else {
	char *line = NULL;
	size_t n = 0;

	while (shapes > 0) {
	    long int parts, points;

	    if (-1 == getline(&line, &n, foo))
		break;
	    sscanf(line, "%*s %*d parts %ld points %ld", &parts, &points);
	    skip_lines(foo, parts - 1 + points);

	    --shapes;
	}
	free(line);
    }
}

void get_boundingbox(void)
{
    while (!feof(shapes)) {
	long nextshapenum; /* The next shape we care about. */
	long parts, points;
	long i;

	if (-1 == getline(&numbersline, &numberslinesize, numbers))
	    break;
	sscanf(numbersline, "%ld", &nextshapenum);

	skip_shapes(shapes, nextshapenum - shapenum);
	shapenum = nextshapenum;

	getline(&shapesline, &shapeslinesize, shapes);

	if (strcmp(shapesline, "end\n") == 0)
	    break;

	sscanf(shapesline, "%*s %*d parts %ld points %ld", &parts, &points);

	for (i = 0; i < parts - 1 + points; ++i) {
	    int xi, xd, yi, yd, xcoord, ycoord;

	    getline(&shapesline, &shapeslinesize, shapes);

	    if (strcmp(shapesline, "part\n") == 0)
		continue;

	    sscanf(shapesline, "%d.%d\t%d.%d", &xi, &xd, &yi, &yd);
	    xcoord = dig * xi + sign(xi) * xd;
	    ycoord = -1 * (dig * yi + sign(yi) * yd);

	    if (xi == 0 && shapesline[0] == '-')
		xcoord *= -1;
//	    fprintf(stderr, "yi = %d, strchr(shapesline, tab) = %d, shapesline = '%s'\n", yi, strchr(shapesline, '\t') - shapesline, shapesline);
	    if (yi == 0 && *(strchr(shapesline, '\t') + 1) == '-')
		ycoord *= -1;

	    if (xcoord < minx)
		minx = xcoord;
	    if (ycoord < miny)
		miny = ycoord;
	    if (xcoord > maxx)
		maxx = xcoord;
	    if (ycoord > maxy)
		maxy = ycoord;
	}
	++shapenum;
    }
}

void process_part(long *currentx, long *currenty, long *points)
{
    bool startpart = true;
    bool bufferfull = false;
    long x1 = 0, y1 = 0;  // The first point.
    long xwrote = 0, ywrote = 0; // The last point we wrote.
    long xbuf, ybuf; // A point in a buffer, might not be written.

    if (*points == 0)
	return;

    while (*points > 0) {
	long xi, xd, yi, yd, xcoord, ycoord;

	getline(&shapesline, &shapeslinesize, shapes);

	if (strcmp(shapesline, "part\n") == 0)
	    break;

	(*points)--;

	sscanf(shapesline, " %ld.%ld %ld.%ld", &xi, &xd, &yi, &yd);
	xcoord = dig * xi + sign(xi) * xd;
	ycoord = -1 * (dig * yi + sign(yi) * yd);

	if (startpart) {
	    startpart = false;
	    x1 = xwrote = xcoord;
	    y1 = ywrote = ycoord;
	    printf("m %ld,%ld l", xcoord - *currentx, ycoord - *currenty);
	} else {
	    if (!bufferfull)
		bufferfull = true;
	    else if ((xbuf - xwrote) * (ycoord - ybuf) != (ybuf - ywrote) * (xcoord - xbuf)) {
		printf(" %ld,%ld", xbuf - xwrote, ybuf - ywrote);
		xwrote = xbuf;
		ywrote = ybuf;
	    } else {
//		fprintf(stderr, "dropped: (%ld,%ld) - (%ld,%ld) - (%ld,%ld)\n", xwrote, ywrote, xbuf, ybuf, xcoord, ycoord);
		dropped++;
	    }
	    xbuf = xcoord;
	    ybuf = ycoord;
	}
    }
    if (shapetype == LINE || (xbuf - xwrote) * (y1 - ybuf) != (ybuf - ywrote) * (x1 - xbuf))
	printf(" %ld,%ld", xbuf - xwrote, ybuf - ywrote);
    else {
//	fprintf(stderr, "dropped: (%ld,%ld) - (%ld,%ld) - (%ld,%ld)\n", xwrote, ywrote, xbuf, ybuf, x1, y1);
	dropped++;
    }
    if (shapetype == POLYGON) {
	printf(" z");
	*currentx = x1;
	*currenty = y1;
    } else {
	*currentx = xbuf;
	*currenty = ybuf;
    }
}

void process_polyline(void)
{
    while (!feof(numbers)) {
	long nextshapenum; /* The next shape we care about. */
	long parts, points;
	size_t nameslinelen;
	long currentx = 0, currenty = 0;
	long i;

	if (-1 == getline(&numbersline, &numberslinesize, numbers))
	    break;
	sscanf(numbersline, "%ld", &nextshapenum);

	skip_shapes(shapes, nextshapenum - shapenum);

	if (names != NULL) {
	    skip_lines(names, nextshapenum - shapenum);

	    nameslinelen = getline(&namesline, &nameslinesize, names);
	    nameslinelen--;
	    namesline[nameslinelen] = '\0';
	}
	shapenum = nextshapenum;

	getline(&shapesline, &shapeslinesize, shapes);

	if (strcmp(shapesline, "end\n") == 0)
	    break;

	sscanf(shapesline, "%*s %*d parts %ld points %ld", &parts, &points);

	printf("<path ");
	fprintf(stderr, "Processing %s\n", namesline);
	if (nameslinelen != 0)
	    printf("id=\"%s\" ", namesline);
	printf("d=\"");
	for (i = 0; i < parts; ++i) {
	    process_part(&currentx, &currenty, &points);
	    if (i + 1 < parts)
		printf(" ");
	}
	printf("\" />\n");

	++shapenum;
    }
}

void process_point(void)
{
    while (!feof(numbers)) {
	long nextshapenum; /* The next shape we care about. */
	size_t nameslinelen = 0;
	int xi, xd, yi, yd, xcoord, ycoord;

	if (-1 == getline(&numbersline, &numberslinesize, numbers))
	    break;
	sscanf(numbersline, "%ld", &nextshapenum);

	skip_lines(shapes, nextshapenum - shapenum);

	if (names != NULL) {
	    skip_lines(names, nextshapenum - shapenum);

	    nameslinelen = getline(&namesline, &nameslinesize, names);
	    nameslinelen--;
	    namesline[nameslinelen] = '\0';
	}
	shapenum = nextshapenum;

	getline(&shapesline, &shapeslinesize, shapes);

	if (strcmp(shapesline, "end\n") == 0)
	    break;

	sscanf(shapesline, " %d.%d %d.%d", &xi, &xd, &yi, &yd);
	xcoord = dig * xi + sign(xi) * xd;
	ycoord = -1 * (dig * yi + sign(yi) * yd);

	printf("<circle ");
	if (nameslinelen > 0)
	    printf("id=\"%s\" ", namesline);
	printf("cx=\"%d\" cy=\"%d\" radius=\"50\" />\n", xcoord, ycoord);

	++shapenum;
    }
}

int main(int argc, char **argv)
{

    shapes = fopen(argv[1], "r");
    numbers = fopen(argv[2], "r");
    if (argc > 3)
	names = fopen(argv[3], "r");

    getline(&shapesline, &shapeslinesize, shapes);

    if (strcmp("type polygon\n", shapesline) == 0)
	shapetype = POLYGON;
    else if (strcmp("type line\n", shapesline) == 0)
	shapetype = LINE;
    else if (strcmp("type point\n", shapesline) == 0)
	shapetype = POINT;
    else {
	*strchr(shapesline, ' ') = '\0';
	fprintf(stderr, "unknown shape type %s on line 1\n", shapesline);
	exit(1);
    }

    if (shapetype != POINT) {
	get_boundingbox();
	printf("Bounding box is (%d, %d) to (%d, %d), width = %d, height = %d\n", minx, miny, maxx, maxy, maxx - minx, maxy - miny);
    }

    fseek(shapes, 0, SEEK_SET);
    fseek(numbers, 0, SEEK_SET);

    getline(&shapesline, &shapeslinesize, shapes); /* Skip the "type polygon" line at the top */

    shapenum = 1;

    if (shapetype == POINT)
	process_point();
    else
	process_polyline();

    fprintf(stderr, "dropped = %d\n", dropped);

    return 0;
}