/*---------------------------------------------------------------------------*\ gpstr2shp.c A translator of GPStrans files into Shapefile format files This program was developed for use with gpsman --- GPS Manager: a manager for GPS receiver data Copyright (c) 2002-2011 Miguel Filgueiras (mig@portugalmail.pt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. ------ This program reads from the standard input; its single argument is the (path to the) base-name for the Shapefile files (.shp, .shx, .dbf) This program uses - shapelib, Copyright (c) 1999 by Frank Warmerdam A similar program, that translates "Generate" format, is - gen2shp.c, Copyright (c) 1999 by Jan-Oliver Wagner The GPStrans format is defined by - GPStrans, Copyright (c) 1995 by Carsten Tschach \*---------------------------------------------------------------------------*/ /* File: gentr2shp.c *\ \* Last modified: 8 June 2011 */ /* Format of GPStrans files: - header: DDD (decimal degrees) is the only position format supported Format: DDD UTC Offset: -6.00 hrs Datum[061]: NAD27 CONUS ||| |||||| ||| 0 1 2 3 4 5 6 0123456789012345678901234567890123456789012345678901234567890123456789 - a single data type per file - possible data types: waypoints, routes, tracks - waypoint files: one or more lines W\t%s\t%s\t%s\t%lf\t%lf with name, comment (40 chars), date (MM/DD/YYYY HH:MM:SS), lat, long - route files: one or more of R\t%d\t%s with route number, comment, followed by lines for route waypoints (as in waypoint files) - track files: tracks are separated by a blank line, each track has one or more lines T\t%s\t%lf\t%lf for time-stamp (MM/DD/YYYY HH:MM:SS), lat, long Generated files: - point or polyline shapes - attributes in data-base: - for waypoints: name, commt (comment) and date as strings - for routes: number and commt (comment) as strings - for tracks: date (time-stamp of first track point) as string */ #include #include /* #define DEBUG 1 */ /* lengths of strings */ #define WPNAMEWD 50 #define WPCOMMTWD 128 #define WPDATEWD 25 #define RTIDWD 50 #define RTCOMMTWD 128 #define TRDATEWD 25 typedef struct wpstrt { char wpname[WPNAMEWD], wpcommt[WPCOMMTWD], wpdate[WPDATEWD]; double wplat, wplong; struct wpstrt *wpnext; } WPDATA, *WPLIST; WPDATA WP; int WPCount = 0, WPNameField, WPCommtField, WPDateField; typedef struct { char rtid[RTIDWD], rtcommt[RTCOMMTWD]; WPLIST rtwps; } RTDATA; RTDATA RT; int RTCount = 0, RTlgth, RTIdField, RTCommtField; typedef struct tpstrt { char tpdate[TRDATEWD]; double tplat, tplong; struct tpstrt *tpnext; } TPDATA, *TPLIST; TPLIST TR; int TRCount = 0, TRlgth, TRDateField; char Buffer[256]; SHPHandle SHPFile; DBFHandle DBFFile; #define WPTYPE SHPT_POINT #define RTTYPE SHPT_ARC #define TRTYPE SHPT_ARC int getline(int skipnl) { char *p = Buffer; int empty = 1; while (! feof(stdin)) { if ((*p++=getchar()) == '\n') { --p; if (skipnl && empty) continue; *p = 0; return 1; } empty = 0; } return 0; } int cpnotab(char **pp, char *dest) /* cp string until tab, return 0 if not found */ { while ((*dest++=**pp) && **pp != '\t') (*pp)++; if (**pp == 0) return 1; *--dest = 0; (*pp)++; return 0; } int badheader() { if (! getline(0)) { fprintf(stderr,"no header found\n"); return(1); } if (strncmp("Format: DDD",Buffer,11)) { fprintf(stderr,"bad format in header: %s\n",Buffer); return(1); } return 0; } int badWP(WPLIST pWP) { char *p = Buffer; if (*p++ != 'W' || *p++ != '\t' || cpnotab(&p,pWP->wpname) || cpnotab(&p,pWP->wpcommt) || cpnotab(&p,pWP->wpdate)) return 1; return sscanf(p,"%lf\t%lf",&pWP->wplat,&pWP->wplong) != 2; } void saveWP(WPLIST wpp) /* save a single WP pointed to by wpp */ { SHPObject *pwpo; int entno; #ifdef DEBUG printf("W\t%s\t%s\t%s\t%lf\t%lf\n",wpp->wpname,wpp->wpcommt,wpp->wpdate, wpp->wplat,wpp->wplong); #endif pwpo = SHPCreateSimpleObject(WPTYPE,1,&wpp->wplong,&wpp->wplat,NULL); entno = SHPWriteObject(SHPFile,-1,pwpo); SHPDestroyObject(pwpo); if (DBFWriteStringAttribute(DBFFile,entno,WPNameField,wpp->wpname) == 0 || DBFWriteStringAttribute(DBFFile,entno,WPCommtField,wpp->wpcommt) == 0 || DBFWriteStringAttribute(DBFFile,entno,WPDateField,wpp->wpdate) == 0) { fprintf(stderr,"failed to write .dbf field, WP #%d\n",WPCount); exit(1); } WPCount++; } void transWPs() { if ((WPNameField=DBFAddField(DBFFile,"name",FTString,WPNAMEWD,0)) == -1 || (WPCommtField=DBFAddField(DBFFile,"commt",FTString,WPCOMMTWD,0)) == -1 || (WPDateField=DBFAddField(DBFFile,"date",FTString,WPDATEWD,0)) == -1) { fprintf(stderr,"failed to add .dbf field\n"); exit(1); } do { if (badWP(&WP)) { fprintf(stderr,"bad WP: %s\n",Buffer); exit(1); } saveWP(&WP); } while (getline(1)); } int badRT() { char *p = Buffer, *q = RT.rtcommt; if (*p++ != 'R' || *p++ != '\t' || cpnotab(&p,RT.rtid)) return 1; while ((*q++=*p++)); return 0; } void saveRT() /* and free list of WPs */ { WPLIST wpp, wpl; SHPObject *prto; int entno, i; double *x, *y; #ifdef DEBUG printf("R\t%s\t%s\n",RT.rtid,RT.rtcommt); wpp = RT.rtwps; while (wpp != NULL) { printf("W\t%s\t%s\t%s\t%lf\t%lf\n",wpp->wpname,wpp->wpcommt,wpp->wpdate, wpp->wplat,wpp->wplong); wpp = wpp->wpnext; } #endif if ((x=(double *) malloc(RTlgth*sizeof(double))) == NULL || (y=(double *) malloc(RTlgth*sizeof(double))) == NULL) { fprintf(stderr,"out of memory, RT #%d\n",RTCount); exit(1); } wpp = RT.rtwps; for(i=0; wpp != NULL; i++) { x[i] = wpp->wplong; y[i] = wpp->wplat; wpl = wpp; wpp = wpp->wpnext; free(wpl); } prto = SHPCreateObject(RTTYPE,RTCount,0,NULL,NULL,RTlgth,x,y,NULL,NULL); entno = SHPWriteObject(SHPFile,-1,prto); SHPDestroyObject(prto); free(x); free(y); if (DBFWriteStringAttribute(DBFFile,entno,RTIdField,RT.rtid) == 0 || DBFWriteStringAttribute(DBFFile,entno,RTCommtField,RT.rtcommt) == 0) { fprintf(stderr,"failed to write .dbf field, RT #%d\n",RTCount); exit(1); } RTCount++; } void transRTs() { WPLIST curr, prev; int on; if ((RTIdField=DBFAddField(DBFFile,"id",FTString,RTIDWD,0)) == -1 || (RTCommtField=DBFAddField(DBFFile,"commt",FTString,RTCOMMTWD,0)) == -1) { fprintf(stderr,"failed to add .dbf field\n"); exit(1); } do { if (badRT()) { fprintf(stderr,"bad RT: %s\n",Buffer); exit(1); } if ((curr=RT.rtwps=(WPLIST) malloc(sizeof(WPDATA))) == NULL) { fprintf(stderr,"out of memory!"); exit(1); } if (! getline(1) || badWP(curr)) { fprintf(stderr,"route without valid WPs"); exit(1); } RTlgth = 0; do { RTlgth++; prev = curr; if ((curr=(WPLIST) malloc(sizeof(WPDATA))) == NULL) { fprintf(stderr,"out of memory!"); exit(1); } prev->wpnext = curr; } while ((on=getline(1)) && ! badWP(curr)); free(curr); prev->wpnext = NULL; saveRT(); } while (on); } int badTP(TPLIST ptp) { char *p = Buffer; if (*p++ != 'T' || *p++ != '\t' || cpnotab(&p,ptp->tpdate)) return 1; return sscanf(p,"%lf\t%lf",&ptp->tplat,&ptp->tplong) != 2; } void saveTR() /* and free TP list (i.e., the track) */ { TPLIST tpp, tpl; SHPObject *ptro; int entno, i; double *x, *y; #ifdef DEBUG tpp = TR; while (tpp != NULL) { printf("T\t%s\t%lf\t%lf\n",tpp->tpdate,tpp->tplat,tpp->tplong); tpp = tpp->tpnext; } putchar('\n'); #endif if ((x=(double *) malloc(TRlgth*sizeof(double))) == NULL || (y=(double *) malloc(TRlgth*sizeof(double))) == NULL) { fprintf(stderr,"out of memory, TR #%d\n",TRCount); exit(1); } tpp = TR; for(i=0; tpp != NULL; i++) { x[i] = tpp->tplong; y[i] = tpp->tplat; tpl = tpp; tpp = tpp->tpnext; free(tpl); } ptro = SHPCreateObject(TRTYPE,TRCount,0,NULL,NULL,TRlgth,x,y,NULL,NULL); entno = SHPWriteObject(SHPFile,-1,ptro); SHPDestroyObject(ptro); free(x); free(y); if (DBFWriteStringAttribute(DBFFile,TRCount,TRDateField,TR->tpdate) == 0) { fprintf(stderr,"failed to write .dbf field, TR #%d\n",TRCount); exit(1); } TRCount++; } void transTRs() { TPLIST curr, prev; if ((TRDateField=DBFAddField(DBFFile,"date",FTString,TRDATEWD,0)) == -1) { fprintf(stderr,"failed to add .dbf field\n"); exit(1); } do { if ((TR=curr=(TPLIST) malloc(sizeof(TPDATA))) == NULL) { fprintf(stderr,"out of memory!"); exit(1); } TRlgth = 0; do { if (badTP(curr)) { fprintf(stderr,"invalid TP in TR: %s\n",Buffer); exit(1); } TRlgth++; prev = curr; if ((curr=(TPLIST) malloc(sizeof(TPDATA))) == NULL) { fprintf(stderr,"out of memory!"); exit(1); } prev->tpnext = curr; } while (getline(0) && Buffer[0]); free(curr); prev->tpnext = NULL; saveTR(); } while (getline(1)); } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr,"Usage: gpstr2shp BASENAME\n"); exit(1); } if (badheader()) exit(1); if (! getline(1)) { fprintf(stderr,"no data found\n"); exit(1); } if ((DBFFile=DBFCreate(argv[1])) == NULL) { fprintf(stderr,"cannot open %s for writing as .dbf\n",argv[1]); exit(1); } switch (Buffer[0]) { case 'W': if ((SHPFile=SHPCreate(argv[1],WPTYPE)) == NULL) { fprintf(stderr,"cannot open %s for writing\n",argv[1]); exit(1); } transWPs(); break; case 'R': if ((SHPFile=SHPCreate(argv[1],RTTYPE)) == NULL) { fprintf(stderr,"cannot open %s for writing\n",argv[1]); exit(1); } transRTs(); break; case 'T': if ((SHPFile=SHPCreate(argv[1],TRTYPE)) == NULL) { fprintf(stderr,"cannot open %s for writing\n",argv[1]); exit(1); } transTRs(); break; default: fprintf(stderr,"bad line: %s\n",Buffer); exit(1); } SHPClose(SHPFile); DBFClose(DBFFile); exit(0); }