/********************************************************************** * $Id: pgsql2shp.c 2513 2006-10-14 14:22:10Z mschaber $ * * PostGIS - Spatial Types for PostgreSQL * http://postgis.refractions.net * Copyright 2001-2003 Refractions Research Inc. * * This is free software; you can redistribute and/or modify it under * the terms of the GNU General Public Licence. See the COPYING file. * ********************************************************************** * * PostGIS to Shapefile converter * * Original Author: Jeff Lounsbury, jeffloun@refractions.net * * Maintainer: Sandro Santilli, strk@refractions.net * **********************************************************************/ static char rcsid[] = "$Id: pgsql2shp.c 2513 2006-10-14 14:22:10Z mschaber $"; #include #include #include #include #include #include /* Solaris9 does not provide stdint.h */ /* #include */ #include #include "libpq-fe.h" #include "shapefil.h" #include "getopt.h" #include "compat.h" #include // for getpid() #include // for getpid() #ifdef __CYGWIN__ #include #endif #define POINTTYPE 1 #define LINETYPE 2 #define POLYGONTYPE 3 #define MULTIPOINTTYPE 4 #define MULTILINETYPE 5 #define MULTIPOLYGONTYPE 6 #define COLLECTIONTYPE 7 #define BBOXONLYTYPE 99 /* * Verbosity: * set to 1 to see record fetching progress * set to 2 to see also shapefile creation progress * set to 3 for debugging */ #define VERBOSE 1 /* Define this to use HEX encoding instead of bytea encoding */ #define HEXWKB 1 /* Maximum DBF field width (according to ARCGIS) */ #define MAX_DBF_FIELD_SIZE 255 /*typedef unsigned long int uint32;*/ typedef uint32_t uint32; typedef unsigned char byte; /* Global data */ PGconn *conn; int rowbuflen; char temptablename[256]; char *geo_col_name, *table, *shp_file, *schema, *usrquery; int type_ary[256]; char *main_scan_query; DBFHandle dbf; SHPHandle shp; int geotype; int outshptype; char outtype; int dswitchprovided; int includegid; int unescapedattrs; int binary; int keep_fieldname_case; SHPObject * (*shape_creator)(byte *, int); int big_endian = 0; int pgis_major_version; /* Prototypes */ int getMaxFieldSize(PGconn *conn, char *schema, char *table, char *fname); int parse_commandline(int ARGC, char **ARGV); void usage(char* me, int exitstatus, FILE* out); char *getTableOID(char *schema, char *table); int addRecord(PGresult *res, int residx, int row); int initShapefile(char *shp_file, PGresult *res); int initialize(void); int getGeometryOID(PGconn *conn); int getGeometryType(char *schema, char *table, char *geo_col_name); int getGeometryMaxDims(char *schema, char *table, char *geo_col_name); char *shapetypename(int num); int parse_points(char *str, int num_points, double *x,double *y,double *z); int num_points(char *str); int num_lines(char *str); char *scan_to_same_level(char *str); int points_per_sublist( char *str, int *npoints, long max_lists); int reverse_points(int num_points, double *x, double *y, double *z, double *m); int is_clockwise(int num_points,double *x,double *y,double *z); int is_bigendian(void); SHPObject * shape_creator_wrapper_WKB(byte *str, int idx); int get_postgis_major_version(void); static void parse_table(char *spec); static int create_usrquerytable(void); static const char *nullDBFValue(char fieldType); /* * Make appropriate formatting of a DBF value based on type. * Might return untouched input or pointer to static private * buffer: use return value right away. */ static const char * goodDBFValue(const char *in, char fieldType); /* WKB functions */ SHPObject * create_polygon2D_WKB(byte *wkb); SHPObject * create_polygon3D_WKB(byte *wkb); SHPObject * create_polygon4D_WKB(byte *wkb); SHPObject * create_multipoint2D_WKB(byte *wkb); SHPObject * create_multipoint3D_WKB(byte *wkb); SHPObject * create_multipoint4D_WKB(byte *wkb); SHPObject * create_point2D_WKB(byte *wkb); SHPObject * create_point3D_WKB(byte *wkb); SHPObject * create_point4D_WKB(byte *wkb); SHPObject * create_multiline2D_WKB (byte *wkb); SHPObject * create_multiline3D_WKB (byte *wkb); SHPObject * create_multiline4D_WKB (byte *wkb); SHPObject * create_line2D_WKB(byte *wkb); SHPObject * create_line3D_WKB(byte *wkb); SHPObject * create_line4D_WKB(byte *wkb); SHPObject * create_multipolygon2D_WKB(byte *wkb); SHPObject * create_multipolygon3D_WKB(byte *wkb); SHPObject * create_multipolygon4D_WKB(byte *wkb); byte getbyte(byte *c); void skipbyte(byte **c); byte popbyte(byte **c); uint32 popint(byte **c); uint32 getint(byte *c); void skipint(byte **c); double popdouble(byte **c); void skipdouble(byte **c); void dump_wkb(byte *wkb); byte * HexDecode(const char *hex); #define WKBZOFFSET 0x80000000 #define WKBMOFFSET 0x40000000 #define ZMFLAG(x) (((x)&((WKBZOFFSET)+(WKBMOFFSET)))>>30) static void exit_nicely(PGconn *conn, int code) { PQfinish(conn); exit(code); } int main(int ARGC, char **ARGV) { char *query=NULL; int row; PGresult *res; char fetchquery[256]; dbf=NULL; shp=NULL; geotype=-1; shape_creator = NULL; table = NULL; schema = NULL; usrquery = NULL; geo_col_name = NULL; shp_file = NULL; main_scan_query = NULL; rowbuflen=100; outtype = 's'; dswitchprovided = 0; includegid=0; unescapedattrs=0; binary = 0; keep_fieldname_case = 0; #ifdef DEBUG FILE *debug; #endif if ( getenv("ROWBUFLEN") ) rowbuflen=atoi(getenv("ROWBUFLEN")); /* * Make sure dates are returned in ISO * style (YYYY-MM-DD). * This is to allow goodDBFValue() function * to successfully extract YYYYMMDD format * expected in shapefile's dbf file. */ putenv("PGDATESTYLE=ISO"); if ( ! parse_commandline(ARGC, ARGV) ) { printf("\n**ERROR** invalid option or command parameters\n\n"); usage(ARGV[0], 2, stderr); } /* Use table name as shapefile name */ if(shp_file == NULL) shp_file = table; /* Make a connection to the specified database, and exit on failure */ conn = PQconnectdb(""); if (PQstatus(conn) == CONNECTION_BAD) { printf( "%s", PQerrorMessage(conn)); exit_nicely(conn, 1); } /* Create temporary table for user query */ if ( usrquery ) { if ( ! create_usrquerytable() ) { exit(2); } } #ifdef DEBUG debug = fopen("/tmp/trace.out", "w"); PQtrace(conn, debug); #endif /* DEBUG */ /* Initialize shapefile and database infos */ fprintf(stdout, "Initializing... "); fflush(stdout); if ( ! initialize() ) exit_nicely(conn, 1); fprintf(stdout, "Done (postgis major version: %d).\n", pgis_major_version); if ( pgis_major_version > 0 && dswitchprovided ) { printf("WARNING: -d switch is useless when dumping from postgis-1.0.0+\n"); } printf("Output shape: %s\n", shapetypename(outshptype)); /* * Begin the transaction * (a cursor can only be defined inside a transaction block) */ res=PQexec(conn, "BEGIN"); if ( ! res || PQresultStatus(res) != PGRES_COMMAND_OK ) { printf( "%s", PQerrorMessage(conn)); exit_nicely(conn, 1); } PQclear(res); /* * Declare a cursor for the main scan query * as set by the initializer function. */ query = (char *)malloc(strlen(main_scan_query)+256); if ( binary ) { sprintf(query, "DECLARE cur BINARY CURSOR FOR %s", main_scan_query); } else { sprintf(query, "DECLARE cur CURSOR FOR %s", main_scan_query); } #if VERBOSE > 2 printf( "MAINSCAN: %s\n", main_scan_query); #endif free(main_scan_query); res = PQexec(conn, query); free(query); if ( ! res || PQresultStatus(res) != PGRES_COMMAND_OK ) { printf( "MainScanQuery: %s", PQerrorMessage(conn)); exit_nicely(conn, 1); } PQclear(res); /* Set the fetch query */ sprintf(fetchquery, "FETCH %d FROM cur", rowbuflen); fprintf(stdout, "Dumping: "); fflush(stdout); /* * Main scan */ row=0; while(1) { int i; /* Fetch next record buffer from cursor */ #if VERBOSE fprintf(stdout, "X"); fflush(stdout); #endif res = PQexec(conn, fetchquery); if ( ! res || PQresultStatus(res) != PGRES_TUPLES_OK ) { printf( "RecordFetch: %s", PQerrorMessage(conn)); exit_nicely(conn, 1); } /* No more rows, break the loop */ if ( ! PQntuples(res) ) { PQclear(res); break; } for(i=0; i 2 printf("End of result, clearing..."); fflush(stdout); #endif PQclear(res); #if VERBOSE > 2 printf("Done.\n"); #endif } printf(" [%d rows].\n", row); if (dbf) DBFClose(dbf); if (shp) SHPClose(shp); #ifdef DEBUG fclose(debug); #endif /* DEBUG */ exit_nicely(conn, 0); return 0; } SHPObject * shape_creator_wrapper_WKB(byte *str, int idx) { byte *ptr = str; uint32 type; int ndims; int wkb_big_endian; /* byte order */ wkb_big_endian = ! popbyte(&ptr); if ( wkb_big_endian != big_endian ) { printf( "Wrong WKB endiannes, dunno how to flip\n"); exit(1); } /* get type */ type = getint(ptr); ndims=2; if ( type&WKBZOFFSET ) ndims++; if ( type&WKBMOFFSET ) { ndims++; } type &= ~WKBZOFFSET; type &= ~WKBMOFFSET; switch(type) { case MULTILINETYPE: if ( ndims == 2 ) return create_multiline2D_WKB(str); else if ( ndims == 3 ) return create_multiline3D_WKB(str); else if ( ndims == 4 ) return create_multiline4D_WKB(str); case LINETYPE: if ( ndims == 2 ) return create_line2D_WKB(str); else if ( ndims == 3 ) return create_line3D_WKB(str); else if ( ndims == 4 ) return create_line4D_WKB(str); case POLYGONTYPE: if ( ndims == 2 ) return create_polygon2D_WKB(str); else if ( ndims == 3 ) return create_polygon3D_WKB(str); else if ( ndims == 4 ) return create_polygon4D_WKB(str); case MULTIPOLYGONTYPE: if ( ndims == 2 ) return create_multipolygon2D_WKB(str); else if ( ndims == 3 ) return create_multipolygon3D_WKB(str); else if ( ndims == 4 ) return create_multipolygon4D_WKB(str); case POINTTYPE: if ( ndims == 2 ) return create_point2D_WKB(str); else if ( ndims == 3 ) return create_point3D_WKB(str); else if ( ndims == 4 ) return create_point4D_WKB(str); case MULTIPOINTTYPE: if ( ndims == 2 ) return create_multipoint2D_WKB(str); else if ( ndims == 3 ) return create_multipoint3D_WKB(str); else if ( ndims == 4 ) return create_multipoint4D_WKB(str); default: printf( "Unknown WKB type (%8.8x) - (%s:%d)\n", type, __FILE__, __LINE__); return NULL; } } /* * Reads WKT points into x,y,z co-ord arrays */ int parse_points(char *str, int num_points, double *x,double *y,double *z){ int keep_going; int num_found= 0; char *end_of_double; if ( (str == NULL) || (str[0] == 0) ){ return 0; /* either null string or empty string */ } /* look ahead for the "(" */ str = strchr(str,'(') ; /* either didnt find "(" or its at the end of the string */ if ( (str == NULL) || (str[1] == 0) ) { /* str[0] = '('; */ return 0; } str++; /*move forward one char */ keep_going = 1; while (keep_going == 1){ /* * attempt to get the point * scanf is slow, so we use strtod() */ x[num_found] = (double)strtod(str,&end_of_double); if (end_of_double == str){ return 0; /*error occured (nothing parsed) */ } str = end_of_double; y[num_found] = strtod(str,&end_of_double); if (end_of_double == str){ return 0; /*error occured (nothing parsed) */ } str = end_of_double; /* strtod() return will be zero if error occured */ z[num_found] = strtod(str,&end_of_double); str = end_of_double; num_found++; str=strpbrk(str,",)"); /* look for a "," or ")" */ if (str != NULL && str[0] == ','){ str++; } keep_going = (str != NULL) && (str[0] != ')'); } return num_found; } /* * Returns how many points are in the first list in str * * 1. scan ahead looking for "(" * 2. find "," until hit a ")" * 3. return number of points found * * NOTE: doesnt actually parse the points, so if the * str contains an invalid geometry, this could give * back the wrong answer. * * "(1 2 3, 4 5 6),(7 8, 9 10, 11 12 13)" => 2 (2nd list is not included) */ int num_points(char *str) { int keep_going; int points_found = 1; /* no "," if only one point (and last point) */ if ( (str == NULL) || (str[0] == 0) ) { return 0; /* either null string or empty string */ } /* look ahead for the "(" */ str = strchr(str,'(') ; /* either didnt find "(" or its at the end of the string */ if ( (str == NULL) || (str[1] == 0) ) /* str[0] = '('; */ { return 0; } keep_going = 1; while (keep_going) { str=strpbrk(str,",)"); /* look for a "," or ")" */ keep_going = (str != NULL); if (keep_going) /* found a , or ) */ { if (str[0] == ')') { /*finished */ return points_found; } else /*str[0] = "," */ { points_found++; str++; /*move 1 char forward */ } } } return points_found; /* technically it should return an error. */ } /* * Number of sublist in a string. * * Find the number of lines in a Multiline * OR * The number of rings in a Polygon * OR * The number of polygons in a multipolygon * * ( (..),(..),(..) ) -> 3 * ( ( (..),(..) ), ( (..) )) -> 2 * ( ) -> 0 * scan through the list, for every "(", depth (nesting) increases by 1 * for every ")", depth (nesting) decreases by 1 * if find a "(" at depth 1, then there is a sub list * * example: * "(((..),(..)),((..)))" *depth 12333223332112333210 * + + increase here */ int num_lines(char *str) { int current_depth = 0; int numb_lists = 0; while ( (str != NULL) && (str[0] != 0) ) { str=strpbrk(str,"()"); /*look for "(" or ")" */ if (str != NULL) { if (str[0] == '(') { current_depth++; if (current_depth == 2) numb_lists ++; } if (str[0] == ')') { current_depth--; if (current_depth == 0) return numb_lists ; } str++; } } return numb_lists ; /* probably should give an error */ } /* * simple scan-forward to find the next "(" at the same level * ( (), (),(), ),(... * + return this location */ char * scan_to_same_level(char *str) { int current_depth = 0; int first_one=1; while ( (str != NULL) && (str[0] != 0) ) { str=strpbrk(str,"()"); if (str != NULL) { if (str[0] == '(') { if (!(first_one)) { if (current_depth == 0) return str; } else { /* ignore the first opening "(" */ first_one = 0; } current_depth++; } if (str[0] == ')') { current_depth--; } str++; } } return str ; /* probably should give an error */ } /* * Find out how many points are in each sublist, put the result in * the array npoints[] * (for at most max_list sublists) * * ( (L1),(L2),(L3) ) --> npoints[0] = points in L1, * npoints[1] = points in L2, * npoints[2] = points in L3 * * We find these by, again, scanning through str looking for "(" and ")" * to determine the current depth. We dont actually parse the points. */ int points_per_sublist( char *str, int *npoints, long max_lists) { /*scan through, noting depth and ","s */ int current_depth = 0; int current_list =-1 ; while ( (str != NULL) && (str[0] != 0) ) { str=strpbrk(str,"(),"); /*find "(" or ")" or "," */ if (str != NULL) { if (str[0] == '(') { current_depth++; if (current_depth == 2) { current_list ++; if (current_list >=max_lists) return 1; /* too many sub lists found */ npoints[current_list] = 1; } /* might want to return an error if depth>2 */ } if (str[0] == ')') { current_depth--; if (current_depth == 0) return 1 ; } if (str[0] == ',') { if (current_depth==2) { npoints[current_list] ++; } } str++; } } return 1 ; /* probably should give an error */ } SHPObject * create_multiline3D_WKB (byte *wkb) { SHPObject *obj; double *x=NULL, *y=NULL, *zm=NULL; int *part_index=NULL, totpoints=0, nlines=0; int li; int zmflag; /* skip byteOrder */ skipbyte(&wkb); /* extract zmflag from type */ zmflag = ZMFLAG(popint(&wkb)); /* * Scan all lines in multiline */ nlines=popint(&wkb); /* num_wkbLineStrings */ #if VERBOSE > 2 printf("Multiline with %d lines\n", nlines); #endif part_index = (int *)malloc(sizeof(int)*(nlines)); for (li=0; li 2 printf("Line %d has %d points\n", li, npoints); #endif x = realloc(x, sizeof(double)*(totpoints+npoints)); y = realloc(y, sizeof(double)*(totpoints+npoints)); zm = realloc(zm, sizeof(double)*(totpoints+npoints)); /* wkb now points at first point */ for (pn=0; pn 2 printf("Multiline with %d lines\n", nlines); #endif part_index = (int *)malloc(sizeof(int)*(nlines)); for (li=0; li 2 printf("Line %d has %d points\n", li, npoints); #endif x = realloc(x, sizeof(double)*(totpoints+npoints)); y = realloc(y, sizeof(double)*(totpoints+npoints)); z = realloc(z, sizeof(double)*(totpoints+npoints)); m = realloc(m, sizeof(double)*(totpoints+npoints)); /* wkb now points at first point */ for (pn=0; pn 2 printf("Multiline with %d lines\n", nlines); #endif part_index = (int *)malloc(sizeof(int)*(nlines)); for (li=0; li 2 printf("Line %d has %d points\n", li, npoints); #endif x = realloc(x, sizeof(double)*(totpoints+npoints)); y = realloc(y, sizeof(double)*(totpoints+npoints)); /* wkb now points at first point */ for (pn=0; pn 2 printf("Line has %lu points\n", npoints); #endif x = malloc(sizeof(double)*(npoints)); y = malloc(sizeof(double)*(npoints)); z = malloc(sizeof(double)*(npoints)); m = malloc(sizeof(double)*(npoints)); /* wkb now points at first point */ for (pn=0; pn 2 printf("Line has %lu points\n", npoints); #endif x = malloc(sizeof(double)*(npoints)); y = malloc(sizeof(double)*(npoints)); zm = malloc(sizeof(double)*(npoints)); /* wkb now points at first point */ for (pn=0; pn 2 printf("Line has %lu points\n", npoints); #endif x = malloc(sizeof(double)*(npoints)); y = malloc(sizeof(double)*(npoints)); /* wkb now points at first point */ for (pn=0; pn 2 printf("Polygon with %d rings\n", nrings); #endif part_index = (int *)malloc(sizeof(int)*nrings); for (ri=0; ri 2 printf("Forcing CW\n"); #endif reverse_points(npoints, x+totpoints, y+totpoints, NULL, NULL); } } else { if ( is_clockwise(npoints, x+totpoints, y+totpoints, NULL) ) { #if VERBOSE > 2 printf("Forcing CCW\n"); #endif reverse_points(npoints, x+totpoints, y+totpoints, NULL, NULL); } } part_index[ri] = totpoints; totpoints += npoints; } obj = SHPCreateObject(outshptype, -1, nrings, part_index, NULL, totpoints, x, y, z, NULL); free(part_index); free(x); free(y); return obj; } SHPObject * create_polygon4D_WKB(byte *wkb) { SHPObject *obj; int ri, nrings, totpoints=0, *part_index=NULL; double *x=NULL, *y=NULL, *z=NULL, *m=NULL; int zmflag; /* skip byteOrder */ skipbyte(&wkb); /* extract zmflag from type */ zmflag = ZMFLAG(popint(&wkb)); /* * Scan all rings */ nrings = popint(&wkb); #if VERBOSE > 2 printf("Polygon with %d rings\n", nrings); #endif part_index = (int *)malloc(sizeof(int)*nrings); for (ri=0; ri 2 printf("Forcing CW\n"); #endif reverse_points(npoints, x+totpoints, y+totpoints, z+totpoints, m+totpoints); } } else { if ( is_clockwise(npoints, x+totpoints, y+totpoints, z+totpoints) ) { #if VERBOSE > 2 printf("Forcing CCW\n"); #endif reverse_points(npoints, x+totpoints, y+totpoints, z+totpoints, m+totpoints); } } part_index[ri] = totpoints; totpoints += npoints; } obj = SHPCreateObject(outshptype, -1, nrings, part_index, NULL, totpoints, x, y, z, m); free(part_index); free(x); free(y); free(z); free(m); return obj; } SHPObject * create_polygon3D_WKB(byte *wkb) { SHPObject *obj; int ri, nrings, totpoints=0, *part_index=NULL; double *x=NULL, *y=NULL, *zm=NULL, *z=NULL; int zmflag; /* skip byteOrder */ skipbyte(&wkb); /* extract zmflag from type */ zmflag = ZMFLAG(popint(&wkb)); /* * Scan all rings */ nrings = popint(&wkb); #if VERBOSE > 2 printf("Polygon with %d rings\n", nrings); #endif part_index = (int *)malloc(sizeof(int)*nrings); for (ri=0; ri 2 printf("Forcing CW\n"); #endif reverse_points(npoints, x+totpoints, y+totpoints, zm+totpoints, NULL); } } else { if ( is_clockwise(npoints, x+totpoints, y+totpoints, z) ) { #if VERBOSE > 2 printf("Forcing CCW\n"); #endif reverse_points(npoints, x+totpoints, y+totpoints, zm+totpoints, NULL); } } part_index[ri] = totpoints; totpoints += npoints; } if ( zmflag == 1 ) { obj = SHPCreateObject(outshptype, -1, nrings, part_index, NULL, totpoints, x, y, NULL, zm); } else { obj = SHPCreateObject(outshptype, -1, nrings, part_index, NULL, totpoints, x, y, zm, NULL); } free(part_index); free(x); free(y); free(zm); return obj; } SHPObject * create_multipolygon2D_WKB(byte *wkb) { SHPObject *obj; uint32 nrings, nparts=0; uint32 npolys; uint32 totpoints=0; int *part_index=NULL; uint32 pi; double *x=NULL, *y=NULL; int zmflag; /* skip byteOrder */ skipbyte(&wkb); /* extract zmflag from type */ zmflag = ZMFLAG(popint(&wkb)); /* * Scan all polygons in multipolygon */ npolys = popint(&wkb); /* num_wkbPolygons */ #if VERBOSE > 2 printf("Multipolygon with %lu polygons\n", npolys); #endif /* * Now wkb points to a WKBPolygon structure */ for (pi=0; pi 2 printf("Polygon %lu has %lu rings\n", pi, nrings); #endif /* wkb now points at first ring */ for (ri=0; ri 2 printf("Ring %lu has %lu points\n", ri, npoints); #endif x = realloc(x, sizeof(double)*(totpoints+npoints)); y = realloc(y, sizeof(double)*(totpoints+npoints)); /* wkb now points at first point */ for (pn=0; pn 3 printf("Point%lu (%f,%f)\n", pn, x[totpoints+pn], y[totpoints+pn]); #endif } /* * First ring should be clockwise, * other rings should be counter-clockwise */ if ( !ri ) { if (!is_clockwise(npoints, x+totpoints, y+totpoints, NULL)) { #if VERBOSE > 2 printf("Forcing CW\n"); #endif reverse_points(npoints, x+totpoints, y+totpoints, NULL, NULL); } } else { if (is_clockwise(npoints, x+totpoints, y+totpoints, NULL)) { #if VERBOSE > 2 printf("Forcing CCW\n"); #endif reverse_points(npoints, x+totpoints, y+totpoints, NULL, NULL); } } part_index[nparts+ri] = totpoints; totpoints += npoints; } #if VERBOSE > 2 printf("End of rings\n"); #endif nparts += nrings; } #if VERBOSE > 2 printf("End of polygons\n"); #endif obj = SHPCreateObject(outshptype, -1, nparts, part_index, NULL, totpoints, x, y, NULL, NULL); #if VERBOSE > 2 printf("Object created\n"); #endif free(part_index); free(x); free(y); return obj; } SHPObject * create_multipolygon3D_WKB(byte *wkb) { SHPObject *obj; int nrings, nparts=0; uint32 npolys; int totpoints=0; int *part_index=NULL; int pi; double *x=NULL, *y=NULL, *z=NULL, *zm=NULL; int zmflag; /* skip byteOrder */ skipbyte(&wkb); /* extract zmflag from type */ zmflag = ZMFLAG(popint(&wkb)); /* * Scan all polygons in multipolygon */ npolys = popint(&wkb); /* num_wkbPolygons */ #if VERBOSE > 2 printf("Multipolygon with %lu polygons\n", npolys); #endif /* * Now wkb points to a WKBPolygon structure */ for (pi=0; pi 2 printf("Polygon %d has %d rings\n", pi, nrings); #endif /* wkb now points at first ring */ for (ri=0; ri 2 printf("Ring %d has %d points\n", ri, npoints); #endif x = realloc(x, sizeof(double)*(totpoints+npoints)); y = realloc(y, sizeof(double)*(totpoints+npoints)); zm = realloc(zm, sizeof(double)*(totpoints+npoints)); /* wkb now points at first point */ for (pn=0; pn 3 printf("Point%d (%f,%f)\n", pn, x[totpoints+pn], y[totpoints+pn]); #endif } /* * First ring should be clockwise, * other rings should be counter-clockwise */ /* Set z to NULL if TYPEM */ if ( zmflag == 1 ) z = NULL; else z = zm+totpoints; if ( !ri ) { if (!is_clockwise(npoints, x+totpoints, y+totpoints, z)) { #if VERBOSE > 2 printf("Forcing CW\n"); #endif reverse_points(npoints, x+totpoints, y+totpoints, zm+totpoints, NULL); } } else { if (is_clockwise(npoints, x+totpoints, y+totpoints, z)) { #if VERBOSE > 2 printf("Forcing CCW\n"); #endif reverse_points(npoints, x+totpoints, y+totpoints, zm+totpoints, NULL); } } part_index[nparts+ri] = totpoints; totpoints += npoints; } #if VERBOSE > 2 printf("End of rings\n"); #endif nparts += nrings; } #if VERBOSE > 2 printf("End of polygons\n"); #endif if ( zmflag == 1 ) { obj = SHPCreateObject(outshptype, -1, nparts, part_index, NULL, totpoints, x, y, NULL, zm); } else { obj = SHPCreateObject(outshptype, -1, nparts, part_index, NULL, totpoints, x, y, zm, NULL); } #if VERBOSE > 2 printf("Object created\n"); #endif free(part_index); free(x); free(y); free(zm); return obj; } SHPObject * create_multipolygon4D_WKB(byte *wkb) { SHPObject *obj; int nrings, nparts=0; uint32 npolys; int totpoints=0; int *part_index=NULL; int pi; double *x=NULL, *y=NULL, *z=NULL, *m=NULL; int zmflag; /* skip byteOrder */ skipbyte(&wkb); /* extract zmflag from type */ zmflag = ZMFLAG(popint(&wkb)); /* * Scan all polygons in multipolygon */ npolys = popint(&wkb); /* num_wkbPolygons */ #if VERBOSE > 2 printf("Multipolygon with %lu polygons\n", npolys); #endif /* * Now wkb points to a WKBPolygon structure */ for (pi=0; pi 2 printf("Polygon %d has %d rings\n", pi, nrings); #endif /* wkb now points at first ring */ for (ri=0; ri 2 printf("Ring %d has %d points\n", ri, npoints); #endif x = realloc(x, sizeof(double)*(totpoints+npoints)); y = realloc(y, sizeof(double)*(totpoints+npoints)); z = realloc(z, sizeof(double)*(totpoints+npoints)); m = realloc(m, sizeof(double)*(totpoints+npoints)); /* wkb now points at first point */ for (pn=0; pn 3 printf("Point%d (%f,%f)\n", pn, x[totpoints+pn], y[totpoints+pn]); #endif } /* * First ring should be clockwise, * other rings should be counter-clockwise */ if ( !ri ) { if (!is_clockwise(npoints, x+totpoints, y+totpoints, z+totpoints)) { #if VERBOSE > 2 printf("Forcing CW\n"); #endif reverse_points(npoints, x+totpoints, y+totpoints, z+totpoints, m+totpoints); } } else { if (is_clockwise(npoints, x+totpoints, y+totpoints, z+totpoints)) { #if VERBOSE > 2 printf("Forcing CCW\n"); #endif reverse_points(npoints, x+totpoints, y+totpoints, z+totpoints, m+totpoints); } } part_index[nparts+ri] = totpoints; totpoints += npoints; } #if VERBOSE > 2 printf("End of rings\n"); #endif nparts += nrings; } #if VERBOSE > 2 printf("End of polygons\n"); #endif obj = SHPCreateObject(outshptype, -1, nparts, part_index, NULL, totpoints, x, y, z, m); #if VERBOSE > 2 printf("Object created\n"); #endif free(part_index); free(x); free(y); free(z); free(m); return obj; } /*Reverse the clockwise-ness of the point list... */ int reverse_points(int num_points, double *x, double *y, double *z, double *m) { int i,j; double temp; j = num_points -1; for(i=0; i 0 ){ free(x_new); free(y_new); return 0; /*counter-clockwise */ }else{ free(x_new); free(y_new); return 1; /*clockwise */ } } /* * Returns OID integer on success * Returns -1 on error. */ int getGeometryOID(PGconn *conn) { PGresult *res1; char *temp_int; int OID; res1=PQexec(conn, "select OID from pg_type where typname = 'geometry'"); if ( ! res1 || PQresultStatus(res1) != PGRES_TUPLES_OK ) { printf( "OIDQuery: %s", PQerrorMessage(conn)); return -1; } if(PQntuples(res1) <= 0 ) { printf( "Geometry type unknown " "(have you enabled postgis?)\n"); return -1; } temp_int = (char *)PQgetvalue(res1, 0, 0); OID = atoi(temp_int); PQclear(res1); return OID; } /* * Passed result is a 1 row result. * Return 1 on success. * Return 0 on failure. */ int addRecord(PGresult *res, int residx, int row) { int j; int nFields = PQnfields(res); int flds = 0; /* number of dbf field */ for (j=0; j 1 fprintf(stdout, "s"); fflush(stdout); #endif if(!DBFWriteAttributeDirectly(dbf, row, flds, val)) { printf("error(string) - Record could not be " "created\n"); return 0; } flds++; continue; } /* If we arrived here it is a geometry attribute */ /* Handle NULL shapes */ if ( PQgetisnull(res, residx, j) ) { obj=SHPCreateSimpleObject(SHPT_NULL,0,NULL,NULL,NULL); if ( SHPWriteObject(shp,-1,obj) == -1) { printf( "Error writing null shape %d\n", row); SHPDestroyObject(obj); return 0; } SHPDestroyObject(obj); continue; } if ( ! binary ) { val = PQgetvalue(res, residx, j); #ifndef HEXWKB v = PQunescapeBytea((byte *)val, &junk); #else if ( pgis_major_version > 0 ) { v = PQunescapeBytea((byte *)val, &junk); } else { v = HexDecode(val); } #endif /* HEXWKB */ #if VERBOSE > 2 dump_wkb(v); #endif /* VERBOSE > 2 */ } else /* binary */ { v = (byte *)PQgetvalue(res, residx, j); } #if VERBOSE > 1 fprintf(stdout, "g"); fflush(stdout); #endif obj = shape_creator_wrapper_WKB(v, row); if ( ! obj ) { printf( "Error creating shape for record %d " "(geotype is %d)\n", row, geotype); return 0; } if ( SHPWriteObject(shp,-1,obj) == -1) { printf( "Error writing shape %d\n", row); SHPDestroyObject(obj); return 0; } SHPDestroyObject(obj); if ( ! binary ) free(v); } #if VERBOSE > 2 printf("Finished adding record %d\n", row); #endif return 1; } /* * Return allocate memory. Free after use. */ char * getTableOID(char *schema, char *table) { PGresult *res3; char *query; char *ret; size_t size; size = strlen(table)+256; if ( schema ) size += strlen(schema)+1; query = (char *)malloc(size); if ( schema ) { sprintf(query, "SELECT oid FROM pg_class c, pg_namespace n WHERE c.relnamespace n.oid AND n.nspname = '%s' AND c.relname = '%s'", schema, table); } else { sprintf(query, "SELECT oid FROM pg_class WHERE relname = '%s'", table); } res3 = PQexec(conn, query); free(query); if ( ! res3 || PQresultStatus(res3) != PGRES_TUPLES_OK ) { printf( "TableOID: %s", PQerrorMessage(conn)); exit_nicely(conn, 1); } if(PQntuples(res3) == 1 ){ ret = strdup(PQgetvalue(res3, 0, 0)); }else if(PQntuples(res3) == 0 ){ printf( "Cannot find relation OID (does table exist?).\n"); PQclear(res3); return NULL; }else{ ret = strdup(PQgetvalue(res3, 0, 0)); printf( "Warning: Multiple relations detected, the program will only dump the first relation.\n"); } PQclear(res3); return ret; } /* * Return geometry type as defined at top file. * Return -1 on error. * Return 0 on unknown or unsupported geometry type. * Set outtype to 'm' or 'z' depending on input type. */ int getGeometryType(char *schema, char *table, char *geo_col_name) { char query[1024]; PGresult *res; char *geo_str; /* the geometry type string */ int multitype=0; int basetype=0; int foundmulti=0; int foundsingle=0; int i; /************************************************** * Get what kind of Geometry type is in the table **************************************************/ if ( schema ) { sprintf(query, "SELECT DISTINCT geometrytype(\"%s\") " "FROM \"%s\".\"%s\" WHERE NOT geometrytype(\"%s\") " "IS NULL", geo_col_name, schema, table, geo_col_name); } else { sprintf(query, "SELECT DISTINCT geometrytype(\"%s\") " "FROM \"%s\" WHERE NOT geometrytype(\"%s\") IS NULL", geo_col_name, table, geo_col_name); } #if VERBOSE > 2 printf( "%s\n",query); #endif res = PQexec(conn, query); if ( ! res || PQresultStatus(res) != PGRES_TUPLES_OK ) { printf( "GeometryType: %s", PQerrorMessage(conn)); return -1; } if (PQntuples(res) == 0) { printf("ERROR: Cannot determine geometry type (empty table).\n"); PQclear(res); return -1; } /* This will iterate max 2 times */ for (i=0; i 0 ) if ( -1 == getGeometryMaxDims(schema, table, geo_col_name) ) return -1; if ( foundmulti ) return multitype; else return basetype; } /* * Set global outtype variable to: * 'm' for 3dm input * 'z' for 3dz or 4d input * 's' for 2d * Return -1 on error, 0 on success. * Call only on postgis >= 1.0.0 */ int getGeometryMaxDims(char *schema, char *table, char *geo_col_name) { char query[1024]; PGresult *res; int maxzmflag; if ( schema ) { sprintf(query, "SELECT max(zmflag(\"%s\")) " "FROM \"%s\".\"%s\"", geo_col_name, schema, table); } else { sprintf(query, "SELECT max(zmflag(\"%s\")) " "FROM \"%s\"", geo_col_name, table); } #if VERBOSE > 2 printf("%s\n",query); #endif res = PQexec(conn, query); if ( ! res || PQresultStatus(res) != PGRES_TUPLES_OK ) { printf( "ZMflagQuery: %s", PQerrorMessage(conn)); PQclear(res); return -1; } if (PQntuples(res) == 0) { printf("ERROR: Cannot determine geometry dimensions (empty table).\n"); PQclear(res); return -1; } maxzmflag = atoi(PQgetvalue(res, 0, 0)); PQclear(res); switch (maxzmflag) { case 0: outtype = 's'; break; case 1: outtype = 'm'; break; default: outtype = 'z'; break; } return 0; } void usage(char* me, int status, FILE* out) { fprintf(out,"RCSID: %s RELEASE: %s\n", rcsid, POSTGIS_VERSION); fprintf(out,"USAGE: %s [] [.]\n", me); fprintf(out," %s [] \n", me); fprintf(out,"\n"); fprintf(out,"OPTIONS:\n"); fprintf(out," -f Use this option to specify the name of the file\n"); fprintf(out," to create.\n"); fprintf(out," -h Allows you to specify connection to a database on a\n"); fprintf(out," machine other than the default.\n"); fprintf(out," -p Allows you to specify a database port other than the default.\n"); fprintf(out," -P Connect to the database with the specified password.\n"); fprintf(out," -u Connect to the database as the specified user.\n"); fprintf(out," -g Specify the geometry column to be exported.\n"); fprintf(out," -b Use a binary cursor.\n"); fprintf(out," -r Raw mode. Do not assume table has been created by \n"); fprintf(out," the loader. This would not unescape attribute names\n"); fprintf(out," and will not skip the 'gid' attribute.\n"); fprintf(out," -k Keep postgresql identifiers case.\n"); fprintf(out," -? Display this help screen.\n"); fprintf(out,"\n"); exit (status); } /* Parse command line parameters */ int parse_commandline(int ARGC, char **ARGV) { if ( ARGC == 1 ) { usage(ARGV[0], 0, stdout); } int c, curindex; char buf[1024]; buf[1023] = '\0'; /* just in case... */ /* Parse command line */ while ((c = getopt(ARGC, ARGV, "bf:h:du:p:P:g:rk")) != EOF){ switch (c) { case 'b': binary = 1; break; case 'f': shp_file = optarg; break; case 'h': /*setenv("PGHOST", optarg, 1); */ snprintf(buf, 255, "PGHOST=%s", optarg); putenv(strdup(buf)); break; case 'd': dswitchprovided = 1; outtype = 'z'; break; case 'r': includegid = 1; unescapedattrs = 1; break; case 'u': /*setenv("PGUSER", optarg, 1); */ snprintf(buf, 255, "PGUSER=%s", optarg); putenv(strdup(buf)); break; case 'p': /*setenv("PGPORT", optarg, 1); */ snprintf(buf, 255, "PGPORT=%s", optarg); putenv(strdup(buf)); break; case 'P': /*setenv("PGPASSWORD", optarg, 1); */ snprintf(buf, 255, "PGPASSWORD=%s", optarg); putenv(strdup(buf)); break; case 'g': geo_col_name = optarg; break; case 'k': keep_fieldname_case = 1; break; case '?': usage(ARGV[0], 0, stdout); default: return 0; } } curindex=0; for (; optind 0 AND c.relname = '%s'", schema, table); } else { sprintf(query, "SELECT a.attname, a.atttypid, a.attlen, " "a.atttypmod FROM " "pg_attribute a, pg_class c WHERE " "a.attrelid = c.oid and a.attnum > 0 AND " "a.atttypid != 0 AND " "c.relname = '%s'", table); } /* Exec query */ #if VERBOSE > 2 printf( "Attribute query: %s\n", query); #endif res = PQexec(conn, query); free(query); if ( ! res || PQresultStatus(res) != PGRES_TUPLES_OK ) { printf( "Querying for attributes: %s", PQerrorMessage(conn)); return 0; } if (! PQntuples(res)) { printf( "Table %s does not exist\n", table); PQclear(res); return 0; } /* Create the dbf file */ dbf = DBFCreate(shp_file); if ( ! dbf ) { printf( "Could not create dbf file\n"); return 0; } /* Get geometry oid */ geo_oid = getGeometryOID(conn); if ( geo_oid == -1 ) { PQclear(res); return 0; } /* * Scan the result setting fields to be returned in mainscan * query, filling the type_ary, and creating .dbf and .shp files. */ for (i=0; i MAX_DBF_FIELD_SIZE ) { fprintf(stderr, "Warning: values of field '%s' " "exceeding maximum dbf field width (%d) " "will be truncated.\n", fname, MAX_DBF_FIELD_SIZE); size = MAX_DBF_FIELD_SIZE; } /*printf( "FIELD_NAME: %s, SIZE: %d\n", field_name, size); */ /* generic type (use string representation) */ if(DBFAddField(dbf, field_name, FTString, size, 0) == -1) { fprintf(stderr, "Error - String field could not " "be created.\n"); return 0; } type_ary[mainscan_nflds]=FTString; mainscan_flds[mainscan_nflds++] = fname; } /* Release dbf field memory */ for (i=0; i 0 ) { sprintf(buf, "asEWKB(setSRID(\"%s\", -1), 'XDR')", mainscan_flds[i]); } else { sprintf(buf, "asbinary(\"%s\", 'XDR')", mainscan_flds[i]); } #else if ( pgis_major_version > 0 ) { sprintf(buf, "asEWKB(setSRID(\"%s\", -1), 'XDR')", mainscan_flds[i]); } else { sprintf(buf, "asbinary(\"%s\", 'XDR')::bytea", mainscan_flds[i]); } #endif } else /* little_endian */ { #ifdef HEXWKB if ( pgis_major_version > 0 ) { sprintf(buf, "asEWKB(setSRID(\"%s\", -1), 'NDR')", mainscan_flds[i]); } else { sprintf(buf, "asbinary(\"%s\", 'NDR')", mainscan_flds[i]); } #else /* ndef HEXWKB */ if ( pgis_major_version > 0 ) { sprintf(buf, "asEWKB(setSRID(\"%s\", -1), 'NDR')", mainscan_flds[i]); } else { sprintf(buf, "asbinary(\"%s\", 'NDR')::bytea", mainscan_flds[i]); } #endif /* def HEXWKB */ } } else { if ( binary ) sprintf(buf, "\"%s\"::text", mainscan_flds[i]); else sprintf(buf, "\"%s\"", mainscan_flds[i]); } strcat(main_scan_query, buf); } if ( schema ) { sprintf(buf, " FROM \"%s\".\"%s\"", schema, table); } else { sprintf(buf, " FROM \"%s\"", table); } strcat(main_scan_query, buf); /* Order by 'gid' (if found) */ if ( gidfound ) { sprintf(buf, " ORDER BY \"gid\""); strcat(main_scan_query, buf); } PQclear(res); return 1; } /* * Return the maximum octet_length from given table. * Return -1 on error. */ int getMaxFieldSize(PGconn *conn, char *schema, char *table, char *fname) { int size; char *query; PGresult *res; /*( this is ugly: don't forget counting the length */ /* when changing the fixed query strings ) */ if ( schema ) { query = (char *)malloc(strlen(fname)+strlen(table)+ strlen(schema)+40); sprintf(query, "select max(octet_length(\"%s\")) from \"%s\".\"%s\"", fname, schema, table); } else { query = (char *)malloc(strlen(fname)+strlen(table)+40); sprintf(query, "select max(octet_length(\"%s\")) from \"%s\"", fname, table); } #if VERBOSE > 2 printf( "maxFieldLenQuery: %s\n", query); #endif res = PQexec(conn, query); free(query); if ( ! res || PQresultStatus(res) != PGRES_TUPLES_OK ) { printf( "Querying for maximum field length: %s", PQerrorMessage(conn)); return -1; } if(PQntuples(res) <= 0 ) { PQclear(res); return -1; } size = atoi(PQgetvalue(res, 0, 0)); PQclear(res); return size; } /* * Input is a NULL-terminated string. * Output is a binary string. */ byte * HexDecode(const char *hex) { byte *ret, *retptr; const char *hexptr; byte byt; int len; len = strlen(hex)/2; ret = (byte *)malloc(len); if ( ! ret ) { printf( "Out of virtual memory\n"); exit(1); } /*printf("Decoding %d bytes", len); fflush(stdout); */ hexptr = hex; retptr = ret; /* for postgis > 0.9.x skip SRID=#; if found */ if ( pgis_major_version > 0 ) { if ( hexptr[0] == 'S' ) { hexptr = strchr(hexptr, ';'); hexptr++; } } while (*hexptr) { /* * All these checks on WKB correctness * can be avoided, are only here because * I keep getting segfaults whereas * bytea unescaping works fine... */ /*printf("%c", *hexptr); */ if ( *hexptr < 58 && *hexptr > 47 ) byt = (((*hexptr)-48)<<4); else if ( *hexptr > 64 && *hexptr < 71 ) byt = (((*hexptr)-55)<<4); else { printf( "Malformed WKB\n"); exit(1); } hexptr++; /*printf("%c", *hexptr); */ if ( *hexptr < 58 && *hexptr > 47 ) byt |= ((*hexptr)-48); else if ( *hexptr > 64 && *hexptr < 71 ) byt |= ((*hexptr)-55); else { printf("Malformed WKB\n"); exit(1); } hexptr++; /*printf("(%d)", byt); */ *retptr = (byte)byt; retptr++; } /*printf(" Done.\n"); */ return ret; } int is_bigendian(void) { int test = 1; if ( (((char *)(&test))[0]) == 1) { return 0; /*NDR (little_endian) */ } else { return 1; /*XDR (big_endian) */ } } /********************************************************************* * * The following functions might go in a wkb lib * * Right now they work on a binary representation, they * might work on an exadecimal representation of it as returned * by text cursors by postgis. * *********************************************************************/ void dump_wkb(byte *wkb) { int byteOrder; int type; printf("\n-----\n"); byteOrder = popbyte(&wkb); if ( byteOrder == 0 ) printf("ByteOrder: XDR\n"); else if ( byteOrder == 1 ) printf("ByteOrder: NDR\n"); else printf("ByteOrder: unknown (%d)\n", byteOrder); type = popint(&wkb); if ( type&WKBZOFFSET ) printf ("Has Z!\n"); if ( type&WKBMOFFSET ) printf ("Has M!\n"); type &= ~WKBZOFFSET; /* strip Z flag */ type &= ~WKBMOFFSET; /* strip M flag */ printf ("Type: %x\n", type); printf("-----\n"); } void skipbyte(byte **c) { *c+=1; } byte getbyte(byte *c) { return *((char*)c); } byte popbyte(byte **c) { return *((*c)++); } uint32 popint(byte **c) { uint32 i=0; memcpy(&i, *c, 4); *c+=4; return i; } uint32 getint(byte *c) { uint32 i=0; memcpy(&i, c, 4); return i; } void skipint(byte **c) { *c+=4; } double popdouble(byte **c) { double d=0.0; memcpy(&d, *c, 8); *c+=8; return d; } void skipdouble(byte **c) { *c+=8; } char * shapetypename(int num) { switch(num) { case SHPT_NULL: return "Null Shape"; case SHPT_POINT: return "Point"; case SHPT_ARC: return "PolyLine"; case SHPT_POLYGON: return "Polygon"; case SHPT_MULTIPOINT: return "MultiPoint"; case SHPT_POINTZ: return "PointZ"; case SHPT_ARCZ: return "PolyLineZ"; case SHPT_POLYGONZ: return "PolygonZ"; case SHPT_MULTIPOINTZ: return "MultiPointZ"; case SHPT_POINTM: return "PointM"; case SHPT_ARCM: return "PolyLineM"; case SHPT_POLYGONM: return "PolygonM"; case SHPT_MULTIPOINTM: return "MultiPointM"; case SHPT_MULTIPATCH: return "MultiPatch"; default: return "Unknown"; } } /* * Either get a table (and optionally a schema) * or a query. * A query starts with a "select" or "SELECT" string. */ static void parse_table(char *spec) { char *ptr; /* Spec is a query */ if ( strstr(spec, "SELECT ") || strstr(spec, "select ") ) { usrquery = spec; /* * encode pid in table name to reduce * clashes probability (see bug#115) */ sprintf(temptablename, "__pgsql2shp%lu_tmp_table", (long)getpid()); table = temptablename; } else { table = spec; if ( (ptr=strchr(table, '.')) ) { *ptr = '\0'; schema = table; table = ptr+1; } } } static int create_usrquerytable(void) { char *query; PGresult *res; query = malloc(strlen(table)+strlen(usrquery)+32); sprintf(query, "CREATE TEMP TABLE \"%s\" AS %s", table, usrquery); printf("Preparing table for user query... "); fflush(stdout); res = PQexec(conn, query); free(query); if ( ! res || PQresultStatus(res) != PGRES_COMMAND_OK ) { printf( "Failed: %s\n", PQerrorMessage(conn)); return 0; } PQclear(res); printf("Done.\n"); return 1; } /* This is taken and adapted from dbfopen.c of shapelib */ static const char * nullDBFValue(char fieldType) { switch(fieldType) { case FTInteger: case FTDouble: /* NULL numeric fields have value "****************" */ return "****************"; case FTDate: /* NULL date fields have value "00000000" */ return "00000000"; case FTLogical: /* NULL boolean fields have value "?" */ return "?"; default: /* empty string fields are considered NULL */ return ""; } } /* * Make appropriate formatting of a DBF value based on type. * Might return untouched input or pointer to static private * buffer: use return value right away. */ static const char * goodDBFValue(const char *in, char fieldType) { /* * We only work on FTLogical and FTDate. * FTLogical is 1 byte, FTDate is 8 byte (YYYYMMDD) * We allocate space for 9 bytes to take * terminating null into account */ static char buf[9]; switch (fieldType) { case FTLogical: buf[0] = toupper(in[0]); buf[1]='\0'; return buf; case FTDate: buf[0]=in[0]; /* Y */ buf[1]=in[1]; /* Y */ buf[2]=in[2]; /* Y */ buf[3]=in[3]; /* Y */ buf[4]=in[5]; /* M */ buf[5]=in[6]; /* M */ buf[6]=in[8]; /* D */ buf[7]=in[9]; /* D */ buf[8]='\0'; return buf; default: return in; } } /********************************************************************** * $Log$ * Revision 1.85 2006/06/16 14:12:16 strk * - BUGFIX in pgsql2shp successful return code. * - BUGFIX in shp2pgsql handling of MultiLine WKT. * * Revision 1.84 2006/04/18 14:09:28 strk * Limited text field size to 255 (bug #84) [will eventually provide a switch to support wider fields ] * * Revision 1.83 2006/02/03 20:53:36 strk * Swapped stdint.h (unavailable on Solaris9) with inttypes.h * * Revision 1.82 2006/01/16 10:42:57 strk * Added support for Bool and Date DBF<=>PGIS mapping * * Revision 1.81 2006/01/09 16:40:16 strk * ISO C90 comments, signedness mismatch fixes * * Revision 1.80 2005/10/24 11:30:59 strk * * Fixed a bug in string attributes handling truncating values of maximum * allowed length, curtesy of Lars Roessiger. * Reworked integer attributes handling to be stricter in dbf->sql mapping * and to allow for big int8 values in sql->dbf conversion * * Revision 1.79 2005/10/03 18:08:55 strk * Stricter string attributes lenght handling. DBF header will be used * to set varchar maxlenght, (var)char typmod will be used to set DBF header * len. * * Revision 1.78 2005/07/22 19:15:28 strk * Fixed bug in {get,pop}{int,double} for 64bit archs * * Revision 1.77 2005/07/12 16:19:35 strk * Fixed bug in user query handling, reported by Andrew Seales * * Revision 1.76 2005/05/16 17:22:43 strk * Fixed DBF field names handling as for clashes avoiding. * pgsql field renames are warned. * * Revision 1.75 2005/05/13 14:06:24 strk * Applied patch from Obe, Regina to keep identifiers case. * * Revision 1.74 2005/03/25 18:43:07 strk * Fixed PQunescapeBytearea argument (might give problems on 64bit archs) * * Revision 1.73 2005/03/08 11:06:33 strk * modernized old-style parameter declarations * * Revision 1.72 2005/03/04 14:54:03 strk * Fixed bug in multiline handling. * * Revision 1.71 2005/01/31 22:15:22 strk * Added maintainer notice, to reduce Jeff-strk mail bounces * * Revision 1.70 2004/12/22 10:29:09 strk * Drop useless SRID from geometry when downloading EWKB format. * * Revision 1.69 2004/12/15 08:46:47 strk * Fixed memory leaks depending on input size. * * Revision 1.68 2004/11/18 18:14:19 strk * Added a copy of the PQunescapeBytea function found in libpq of PG>=73 * * Revision 1.67 2004/10/17 12:16:47 strk * fixed prototype for user query table * * Revision 1.66 2004/10/17 12:15:10 strk * Bug fixed in multipoint4D creation * * Revision 1.65 2004/10/15 08:26:03 strk * Fixed handling of mixed dimensioned geometries in source table. * * Revision 1.64 2004/10/14 09:59:51 strk * Added support for user query (replacing schema.table) * * Revision 1.63 2004/10/11 14:34:40 strk * Added endiannes specification for postgis-1.0.0+ * * Revision 1.62 2004/10/07 21:51:05 strk * Fixed a bug in 4d handling * * Revision 1.61 2004/10/07 17:15:28 strk * Fixed TYPEM handling. * * Revision 1.60 2004/10/07 06:54:24 strk * cleanups * * Revision 1.59 2004/10/06 17:04:38 strk * ZM handling. Log trimmed. * * Revision 1.58 2004/09/23 16:14:19 strk * Added -m / -z switches to control output type: XYM,XYMZ. * * Revision 1.57 2004/09/20 17:11:44 strk * Added -d -d availability notice in help string. * Added user notice about output shape type. * * Revision 1.56 2004/09/20 16:33:05 strk * Added 4d geometries support. * Changelog section moved at bottom file. * * Revision 1.55 2004/09/20 14:14:43 strk * Fixed a bug in popbyte. Trapped WKB endiannes errors. * * Revision 1.54 2004/09/20 13:49:27 strk * Postgis-1.x support (LWGEOM) added. * postgis version detected at runtime. * Endiannes unchecked ... TODO. * **********************************************************************/