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(¤tx, ¤ty, &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; }