//compile line for Refractions' Solaris machine... //gcc -g -I/data3/postgresql-7.1.2/include -L/data3/postgresql-7.1.2/lib dump.c ../shapelib-1.2.8/shpopen.o ../shapelib-1.2.8/dbfopen.o -o dump -lpq #include #include #include #include #include "libpq-fe.h" #include "shapefil.h" #include "getopt.h" static void exit_nicely(PGconn *conn){ PQfinish(conn); exit(1); } int create_lines(char *str,int shape_id, SHPHandle shp,int dims); int create_multilines(char *str,int shape_id, SHPHandle shp,int dims); int create_points(char *str,SHPHandle shp,int dims); int create_multipoints(char *str, SHPHandle shp,int dims); int create_polygons(char *str,int shape_id, SHPHandle shp,int dims); int create_multipolygons(char *str,int shape_id, SHPHandle shp,int dims); 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); //main //USAGE: pgsql2shp [] //OPTIONS: // -d Set the dump file to 3 dimensions, if this option is not used // all dumping will be 2d only. // -f Use this option to specify the name of the file // to create. // -h Allows you to specify connection to a database on a // machine other than the localhost. // -p Allows you to specify a database port other than 5432. // -P Connect to the database with the specified password. // -u Connect to the database as the specified user. // -g Specify the geometry column to be exported. int main(int ARGC, char **ARGV){ char *pghost,*pgport,*pgoptions,*dbName,*pgpass, *query,*query1,*geo_str,*geo_str_left, *geo_col_name,*geo_OID, conn_string[512],field_name[32],table_OID[16], *shp_file,*pguser, *table; int nFields, is3d, c, errflg, curindex; int i,j,type,size,flds; int type_ary[256]; int OID,geovalue_field; DBFHandle dbf; SHPHandle shp; PGconn *conn; PGresult *res,*res2,*res3; table = NULL; geo_col_name = NULL; geo_str = NULL; dbName = NULL; pghost = NULL; shp_file = NULL; pgport = NULL; geovalue_field = -1; pguser = ""; pgpass = ""; is3d = 0; errflg = 0; OID = 0; while ((c = getopt(ARGC, ARGV, "f:h:du:p:P:g:")) != EOF){ switch (c) { case 'f': shp_file = optarg; break; case 'h': pghost=optarg; break; case 'd': is3d = 1; break; case 'u': pguser = optarg; break; case 'p': pgport = optarg; break; case 'P': pgpass = optarg; break; case 'g': geo_col_name = optarg; break; case '?': errflg=1; } } curindex=0; for ( ; optind < ARGC; optind++){ if(curindex ==0){ dbName = ARGV[optind]; }else if(curindex == 1){ table = ARGV[optind]; } curindex++; } if(curindex != 2){ errflg = 1; } if (errflg==1) { printf("\n**ERROR** invalid option or command parameters\n"); printf("\n"); printf("USAGE: pgsql2shp []
\n"); printf("\n"); printf("OPTIONS:\n"); printf(" -d Set the dump file to 3 dimensions, if this option is not used\n"); printf(" all dumping will be 2d only.\n"); printf(" -f Use this option to specify the name of the file\n"); printf(" to create.\n"); printf(" -h Allows you to specify connection to a database on a\n"); printf(" machine other than the localhost.\n"); printf(" -p Allows you to specify a database port other than 5432.\n"); printf(" -P Connect to the database with the specified password.\n"); printf(" -u Connect to the database as the specified user.\n"); printf(" -g Specify the geometry column to be exported.\n"); printf("\n"); exit (2); } if(shp_file == NULL){ shp_file = malloc(strlen(table) + 1); strcpy(shp_file,table); } if(pgport == NULL){ pgport = "5432"; } if(pghost == NULL){ pghost = "localhost"; } if(strcmp(pgpass,"")==0 && strcmp(pguser,"")==0){ pgoptions = malloc(1); strcpy(pgoptions,""); }else{ pgoptions = malloc(strlen(pguser) + strlen(pgpass) + 20); if(strcmp(pguser,"")!=0){ strcpy(pgoptions,"user="); strcat(pgoptions,pguser); } if(strcmp(pgpass,"") !=0 ){ strcat(pgoptions," password="); strcat(pgoptions,pgpass); } } printf(conn_string); /* make a connection to the specified database */ sprintf(conn_string,"host=%s %s port=%s dbname=%s",pghost,pgoptions,pgport,dbName); conn = PQconnectdb( conn_string ); /* check to see that the backend connection was successfully made */ if (PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr, "Connection to database '%s' failed.\n", dbName); fprintf(stderr, "%s", PQerrorMessage(conn)); exit_nicely(conn); } #ifdef DEBUG debug = fopen("/tmp/trace.out", "w"); PQtrace(conn, debug); #endif /* DEBUG */ //------------------------------------------------------------ //Get the OID of the geometry type query1 = (char *)malloc(strlen("select OID, typname from pg_type where typname = 'geometry';")+2); strcpy(query1,"select OID, typname from pg_type where typname = 'geometry';"); res = PQexec(conn, query1); // printf("OID query result: %s\n",PQresultErrorMessage(res)); free (query1); if(PQntuples(res) > 0 ){ char *temp_int = (char *)PQgetvalue(res, 0, 0); OID = atoi(temp_int); }else{ printf("Cannot determine geometry type OID, Data-Dump Failed."); exit_nicely(conn); } PQclear(res); //------------------------------------------------------------ //Get the table from the DB query= (char *)malloc(strlen(table) + strlen("select * from ")+2); strcpy(query, "select * from ") ; strcat(query, table); // printf("%s\n",query); res = PQexec(conn, query); if(PQntuples(res) > 0 ){ }else{ printf("Invalid table: '%s' (check spelling and existance of >0 tuples).\nData-Dump Failed.",table); exit_nicely(conn); } //printf("Select * query result: %s\n",PQresultErrorMessage(res)); free (query); //------------------------------------------------------------ //Create the dbf file dbf = DBFCreate(shp_file); if(dbf == NULL){ printf("DBF could not be created - Dump FAILED."); exit_nicely(conn); } // add the fields to the DBF nFields = PQnfields(res); flds =0; //keep track of how many fields you have actually created for (i = 0; i < nFields; i++){ if(strlen(PQfname(res, i)) <32){ strcpy(field_name, PQfname(res, i)); }else{ printf("field name %s is too long, must be less than 32 characters.\n",PQfname(res, i)); exit_nicely(conn); } for(j=0;j 0 ){ char *temp_int = (char *)PQgetvalue(res2, 0, 0); size = atoi(temp_int); }else{ size = 32; } } if(type == 20 || type == 21 || type == 22 || type == 23){ if(DBFAddField(dbf, field_name,FTInteger,16,0) == -1)printf("error - Field could not be created.\n"); type_ary[i]=1; flds++; }else if(type == 700 || type == 701){ if(DBFAddField(dbf, field_name,FTDouble,32,10) == -1)printf("error - Field could not be created.\n"); type_ary[i]=2; flds++; }else if(type == OID){ if( geovalue_field == -1){ geovalue_field = i; flds++; }else if(geo_col_name != NULL && !strcasecmp(geo_col_name, field_name)){ geovalue_field = i; } type_ary[i]=9; //the geometry type field }else{ if(DBFAddField(dbf, field_name,FTString,size,0) == -1)printf("error - Field could not be created.\n"); type_ary[i]=3; flds++; } } /* next, print out the instances */ for (i = 0; i < PQntuples(res); i++) { if(i%50 ==1){ printf("DBF tuple - %d added\n",i); } flds = 0; for (j = 0; j < nFields; j++){ if(type_ary[j] == 1){ char *temp_int = (char *)PQgetvalue(res, i, j); int temp = atoi(temp_int); if(DBFWriteIntegerAttribute(dbf, i, flds,temp)== 0)printf("error(int) - Record could not be created\n"); flds++; }else if(type_ary[j] == 2){ char *temp_dbl = PQgetvalue(res, i, j); double temp = atof(temp_dbl); if(DBFWriteDoubleAttribute( dbf, i, flds,temp)== 0)printf("error(double) - Record could not be created\n"); flds++; }else if(type_ary[j] == 9){ //this is the geometry type field, would do all the .shp and .shx stuff here i imagine }else{ char *temp = (char *)malloc(strlen(PQgetvalue(res, i, j))*8); temp = (char *)PQgetvalue(res, i, j); if(DBFWriteStringAttribute( dbf, i, flds,temp)== 0)printf("error(string) - Record could not be created\n"); flds++; } } } printf("DBF tuple - %d added\n",i-1); if(flds==0){ printf("WARNING: There were no fields in the database. The DBF was not created properly, please add a field to the database and try again."); } DBFClose(dbf); //-------------------------------------------------------------------- //Now parse the geo_value (All your base belong to us. For great justice.) //field into the shx and shp files query= (char *)malloc(strlen("select OID from pg_class where relname = ' '")+strlen(table)+2); strcpy(query, "select OID from pg_class where relname = '") ; strcat(query, table); strcat(query, "'"); res3 = PQexec(conn, query); if(PQntuples(res3) == 1 ){ strncpy(table_OID, (PQgetvalue(res3, 0,0)), 15 ); }else if(PQntuples(res3) == 0 ){ printf("ERROR: Cannot determine relation OID.\n"); exit_nicely(conn); }else{ strncpy(table_OID, (PQgetvalue(res3, 0,0)), 15 ); printf("Warning: Multiple relations detected, the program will only dump the first relation.\n"); } //get the geometry column if(geo_col_name == NULL){ query= (char *)malloc(strlen("select attname from pg_attribute where attrelid = and atttypid = ")+38); strcpy(query, "select attname from pg_attribute where attrelid = "); strcat(query, table_OID); strcat(query, " and atttypid = "); geo_OID = (char *)malloc(34); sprintf(geo_OID, "%i", OID); strcat(query, geo_OID ); }else{ query = (char *)malloc(strlen("select attname from pg_attribute where attrelid = and atttypid = ")+ strlen("and attname = ''")+strlen(geo_col_name)+38); strcpy(query, "select attname from pg_attribute where attrelid = "); strcat(query, table_OID); strcat(query, " and atttypid = "); geo_OID = (char *)malloc(34); sprintf(geo_OID, "%i", OID); strcat(query, geo_OID ); strcat(query, " and attname = '"); strcat(query, geo_col_name); strcat(query, "'"); } res3 = PQexec(conn, query); if(PQntuples(res3) == 1 ){ geo_col_name = (char *)malloc(strlen(PQgetvalue(res3, 0,0)) +2 ); geo_col_name = PQgetvalue(res3,0,0); }else if(PQntuples(res3) == 0 ){ if(geo_col_name == NULL){ printf("ERROR: Cannot determine name of geometry column.\n"); }else{ printf("ERROR: Wrong geometry column name.\n"); } exit_nicely(conn); }else{ geo_col_name = (char *)malloc(strlen(PQgetvalue(res3, 0,0)) +2 ); geo_col_name = PQgetvalue(res3,0,0); printf("Warning: Multiple geometry columns detected, the program will only dump the first geometry.\n"); } //get what kind of Geometry type is in the table query= (char *)malloc(strlen(table) + strlen("select distinct (geometrytype()) from ")+18); strcpy(query, "select distinct (geometrytype("); strcat(query, geo_col_name); strcat(query, ")) from ") ; strcat(query, table); res3 = PQexec(conn, query); if(PQntuples(res3) == 1 ){ geo_str = (char *)malloc(strlen(PQgetvalue(res3, 0, 0))+1); memcpy(geo_str, (char *)PQgetvalue(res3, 0, 0),strlen(PQgetvalue(res3,0,0))+1); }else if(PQntuples(res3) > 1 ){ printf("ERROR: Cannot have multiple geometry types in a shapefile.\nUse option -t(unimplemented currently,sorry...) to specify what type of geometry you want dumped\n\n"); exit_nicely(conn); }else{ printf("ERROR: Cannot determine geometry type of table. \n"); exit_nicely(conn); } geo_str_left = (char *)malloc(10); strncpy(geo_str_left,geo_str,8); free(geo_str); if(strncmp(geo_str_left,"MULTILIN",8)==0 ){ //multilinestring --------------------------------------------------- if(is3d == 0){ shp = SHPCreate(shp_file, SHPT_ARC );//2d line shp file }else{ shp = SHPCreate(shp_file, SHPT_ARCZ );//3d line shp file } for(i=0;i 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,'(') ; if ( (str == NULL) || (str[1] == 0) ) // str[0] = '('; { return 0; //either didnt find "(" or its at the end of the string } 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){ //scan forward in string looking for at "(" at the same level // as the one its already pointing at 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 first_one = 0; //ignore the first opening "(" 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 } int create_multilines(char *str,int shape_id, SHPHandle shp,int dims){ int lines,i,j,max_points,index; int *points; int *part_index; double *x; double *y; double *z; double *totx; double *toty; double *totz; SHPObject *obj; lines = num_lines(str); points = (int *)malloc(sizeof(int) * lines); if(points_per_sublist(str, points, lines) ==0){ printf("error - points_per_sublist failed"); } max_points = 0; for(j=0;j2 && str[0] =='(' && str[1] == '(' && str[2] =='('){ str++; } rings = num_lines(str); points = (int *)malloc(sizeof(int) * rings); if(points_per_sublist(str, points, rings) ==0){ printf("error - points_per_sublist failed"); } max_points = 0; for(j=0;j