1 /*---------------------------------------------------------------------------*\
2    gpstr2shp.c
3 
4    A translator of GPStrans files into Shapefile format files
5 
6    This program was developed for use with
7    gpsman --- GPS Manager: a manager for GPS receiver data
8 
9    Copyright (c) 2002-2013 Miguel Filgueiras (migfilg@t-online.de)
10 
11     This program is free software; you can redistribute it and/or modify
12       it under the terms of the GNU General Public License as published by
13       the Free Software Foundation; either version 3 of the License, or
14       (at your option) any later version.
15 
16     This program is distributed in the hope that it will be useful,
17       but WITHOUT ANY WARRANTY; without even the implied warranty of
18       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19       GNU General Public License for more details.
20 
21       You should have received a copy of the GNU General Public License
22       along with this program.
23 
24 ------
25     This program reads from the standard input; its single argument is
26     the (path to the) base-name for the Shapefile files (.shp, .shx, .dbf)
27 
28     This program uses
29     - shapelib, Copyright (c) 1999 by Frank Warmerdam <warmerda@home.com>
30 
31     A similar program, that translates "Generate" format, is
32     - gen2shp.c, Copyright (c) 1999 by Jan-Oliver Wagner <jan@intevation.de>
33 
34     The GPStrans format is defined by
35     - GPStrans, Copyright (c) 1995 by Carsten Tschach
36     <tschach@zedat.fu-berlin.de>
37 
38 \*---------------------------------------------------------------------------*/
39 
40 /* File: gentr2shp.c *\
41 \* Last modified: 6 October 2013 */
42 
43 
44 /* Format of GPStrans files:
45 
46    - header: DDD (decimal degrees) is the only position format supported
47 
48    Format: DDD  UTC Offset:  -6.00 hrs  Datum[061]: NAD27 CONUS
49            |||              ||||||            |||
50    0         1         2         3         4         5         6
51    0123456789012345678901234567890123456789012345678901234567890123456789
52 
53    - a single data type per file
54    - possible data types: waypoints, routes, tracks
55    - waypoint files: one or more lines
56      W\t%s\t%s\t%s\t%lf\t%lf
57    with name, comment (40 chars), date (MM/DD/YYYY HH:MM:SS), lat, long
58    - route files: one or more of
59      R\t%d\t%s
60    with route number, comment, followed by
61      lines for route waypoints (as in waypoint files)
62    - track files: tracks are separated by a blank line, each track has one
63    or more lines
64      T\t%s\t%lf\t%lf
65    for time-stamp (MM/DD/YYYY HH:MM:SS), lat, long
66 
67    Generated files:
68    - point or polyline shapes
69    - attributes in data-base:
70      - for waypoints: name, commt (comment) and date as strings
71      - for routes: number and commt (comment) as strings
72      - for tracks: date (time-stamp of first track point) as string
73 */
74 
75 #include <libshp/shapefil.h>
76 #include <string.h>
77 
78 /* #define DEBUG 1 */
79 
80 /* lengths of strings */
81 #define WPNAMEWD 50
82 #define WPCOMMTWD 128
83 #define WPDATEWD 25
84 #define RTIDWD 50
85 #define RTCOMMTWD 128
86 #define TRDATEWD 25
87 
88 typedef struct wpstrt {
89   char wpname[WPNAMEWD], wpcommt[WPCOMMTWD], wpdate[WPDATEWD];
90   double wplat, wplong;
91   struct wpstrt *wpnext;
92 } WPDATA, *WPLIST;
93 
94 WPDATA WP;
95 int WPCount = 0, WPNameField, WPCommtField, WPDateField;
96 
97 typedef struct {
98   char rtid[RTIDWD], rtcommt[RTCOMMTWD];
99   WPLIST rtwps;
100 } RTDATA;
101 
102 RTDATA RT;
103 int RTCount = 0, RTlgth, RTIdField, RTCommtField;
104 
105 typedef struct tpstrt {
106   char tpdate[TRDATEWD];
107   double tplat, tplong;
108   struct tpstrt *tpnext;
109 } TPDATA, *TPLIST;
110 
111 TPLIST TR;
112 int TRCount = 0, TRlgth, TRDateField;
113 
114 char Buffer[256];
115 
116 SHPHandle SHPFile;
117 DBFHandle DBFFile;
118 
119 #define WPTYPE SHPT_POINT
120 #define RTTYPE SHPT_ARC
121 #define TRTYPE SHPT_ARC
122 
getline(int skipnl)123 int getline(int skipnl)
124 { char *p = Buffer;
125   int empty = 1;
126 
127   while (! feof(stdin)) {
128     if ((*p++=getchar()) == '\n') {
129       --p;
130       if (skipnl && empty)  continue;
131       *p = 0;
132       return 1;
133     }
134     empty = 0;
135   }
136   return 0;
137 }
138 
cpnotab(char ** pp,char * dest)139 int cpnotab(char **pp, char *dest)
140 /* cp string until tab, return 0 if not found */
141 {
142   while ((*dest++=**pp) && **pp != '\t')  (*pp)++;
143   if (**pp == 0)  return 1;
144   *--dest = 0;  (*pp)++;
145   return 0;
146 }
147 
badheader()148 int badheader()
149 {
150   if (! getline(0)) {
151     fprintf(stderr,"no header found\n");
152     return(1);
153   }
154   if (strncmp("Format: DDD",Buffer,11)) {
155     fprintf(stderr,"bad format in header: %s\n",Buffer);
156     return(1);
157   }
158   return 0;
159 }
160 
badWP(WPLIST pWP)161 int badWP(WPLIST pWP)
162 { char *p = Buffer;
163 
164   if (*p++ != 'W' || *p++ != '\t' || cpnotab(&p,pWP->wpname) ||
165       cpnotab(&p,pWP->wpcommt) || cpnotab(&p,pWP->wpdate))  return 1;
166   return sscanf(p,"%lf\t%lf",&pWP->wplat,&pWP->wplong) != 2;
167 }
168 
saveWP(WPLIST wpp)169 void saveWP(WPLIST wpp)  /* save a single WP pointed to by wpp */
170 { SHPObject *pwpo;
171   int entno;
172 
173 #ifdef DEBUG
174   printf("W\t%s\t%s\t%s\t%lf\t%lf\n",wpp->wpname,wpp->wpcommt,wpp->wpdate,
175 	 wpp->wplat,wpp->wplong);
176 #endif
177 
178   pwpo = SHPCreateSimpleObject(WPTYPE,1,&wpp->wplong,&wpp->wplat,NULL);
179   entno = SHPWriteObject(SHPFile,-1,pwpo);
180   SHPDestroyObject(pwpo);
181 
182   if (DBFWriteStringAttribute(DBFFile,entno,WPNameField,wpp->wpname) == 0 ||
183       DBFWriteStringAttribute(DBFFile,entno,WPCommtField,wpp->wpcommt) == 0 ||
184       DBFWriteStringAttribute(DBFFile,entno,WPDateField,wpp->wpdate) == 0) {
185     fprintf(stderr,"failed to write .dbf field, WP #%d\n",WPCount);
186     exit(1);
187   }
188   WPCount++;
189 }
190 
transWPs()191 void transWPs()
192 {
193   if ((WPNameField=DBFAddField(DBFFile,"name",FTString,WPNAMEWD,0)) == -1 ||
194       (WPCommtField=DBFAddField(DBFFile,"commt",FTString,WPCOMMTWD,0)) == -1 ||
195       (WPDateField=DBFAddField(DBFFile,"date",FTString,WPDATEWD,0)) == -1) {
196     fprintf(stderr,"failed to add .dbf field\n");
197     exit(1);
198   }
199   do {
200     if (badWP(&WP)) {
201       fprintf(stderr,"bad WP: %s\n",Buffer);
202       exit(1);
203     }
204     saveWP(&WP);
205   } while (getline(1));
206 }
207 
badRT()208 int badRT()
209 { char *p = Buffer, *q = RT.rtcommt;
210 
211   if (*p++ != 'R' || *p++ != '\t' || cpnotab(&p,RT.rtid))  return 1;
212   while ((*q++=*p++));
213   return 0;
214 }
215 
saveRT()216 void saveRT()   /* and free list of WPs */
217 { WPLIST wpp, wpl;
218   SHPObject *prto;
219   int entno, i;
220   double *x, *y;
221 
222 #ifdef DEBUG
223   printf("R\t%s\t%s\n",RT.rtid,RT.rtcommt);
224   wpp = RT.rtwps;
225   while (wpp != NULL) {
226     printf("W\t%s\t%s\t%s\t%lf\t%lf\n",wpp->wpname,wpp->wpcommt,wpp->wpdate,
227 	   wpp->wplat,wpp->wplong);
228     wpp = wpp->wpnext;
229   }
230 #endif
231 
232   if ((x=(double *) malloc(RTlgth*sizeof(double))) == NULL ||
233       (y=(double *) malloc(RTlgth*sizeof(double))) == NULL) {
234     fprintf(stderr,"out of memory, RT #%d\n",RTCount);
235     exit(1);
236   }
237   wpp = RT.rtwps;
238   for(i=0; wpp != NULL; i++) {
239     x[i] = wpp->wplong;  y[i] = wpp->wplat;
240     wpl = wpp;
241     wpp = wpp->wpnext;
242     free(wpl);
243   }
244   prto = SHPCreateObject(RTTYPE,RTCount,0,NULL,NULL,RTlgth,x,y,NULL,NULL);
245   entno = SHPWriteObject(SHPFile,-1,prto);
246   SHPDestroyObject(prto);
247   free(x);  free(y);
248 
249   if (DBFWriteStringAttribute(DBFFile,entno,RTIdField,RT.rtid) == 0 ||
250       DBFWriteStringAttribute(DBFFile,entno,RTCommtField,RT.rtcommt) == 0) {
251     fprintf(stderr,"failed to write .dbf field, RT #%d\n",RTCount);
252     exit(1);
253   }
254   RTCount++;
255 }
256 
transRTs()257 void transRTs()
258 { WPLIST curr, prev;
259   int on;
260 
261   if ((RTIdField=DBFAddField(DBFFile,"id",FTString,RTIDWD,0)) == -1 ||
262       (RTCommtField=DBFAddField(DBFFile,"commt",FTString,RTCOMMTWD,0)) == -1) {
263     fprintf(stderr,"failed to add .dbf field\n");
264     exit(1);
265   }
266   do {
267     if (badRT()) {
268       fprintf(stderr,"bad RT: %s\n",Buffer);
269       exit(1);
270     }
271     if ((curr=RT.rtwps=(WPLIST) malloc(sizeof(WPDATA))) == NULL) {
272       fprintf(stderr,"out of memory!");
273       exit(1);
274     }
275     if (! getline(1) || badWP(curr)) {
276       fprintf(stderr,"route without valid WPs");
277       exit(1);
278     }
279     RTlgth = 0;
280     do {
281       RTlgth++;
282       prev = curr;
283       if ((curr=(WPLIST) malloc(sizeof(WPDATA))) == NULL) {
284 	fprintf(stderr,"out of memory!");
285 	exit(1);
286       }
287       prev->wpnext = curr;
288     } while ((on=getline(1)) && ! badWP(curr));
289     free(curr);
290     prev->wpnext = NULL;
291     saveRT();
292   } while (on);
293 }
294 
badTP(TPLIST ptp)295 int badTP(TPLIST ptp)
296 { char *p = Buffer;
297 
298   if (*p++ != 'T' || *p++ != '\t' || cpnotab(&p,ptp->tpdate))  return 1;
299   return sscanf(p,"%lf\t%lf",&ptp->tplat,&ptp->tplong) != 2;
300 }
301 
saveTR()302 void saveTR()  /* and free TP list (i.e., the track) */
303 { TPLIST tpp, tpl;
304   SHPObject *ptro;
305   int entno, i;
306   double *x, *y;
307 
308 #ifdef DEBUG
309   tpp = TR;
310   while (tpp != NULL) {
311     printf("T\t%s\t%lf\t%lf\n",tpp->tpdate,tpp->tplat,tpp->tplong);
312     tpp = tpp->tpnext;
313   }
314   putchar('\n');
315 #endif
316 
317   if ((x=(double *) malloc(TRlgth*sizeof(double))) == NULL ||
318       (y=(double *) malloc(TRlgth*sizeof(double))) == NULL) {
319     fprintf(stderr,"out of memory, TR #%d\n",TRCount);
320     exit(1);
321   }
322   tpp = TR;
323   for(i=0; tpp != NULL; i++) {
324     x[i] = tpp->tplong;  y[i] = tpp->tplat;
325     tpl = tpp;
326     tpp = tpp->tpnext;
327     free(tpl);
328   }
329   ptro = SHPCreateObject(TRTYPE,TRCount,0,NULL,NULL,TRlgth,x,y,NULL,NULL);
330   entno = SHPWriteObject(SHPFile,-1,ptro);
331   SHPDestroyObject(ptro);
332   free(x);  free(y);
333 
334   if (DBFWriteStringAttribute(DBFFile,TRCount,TRDateField,TR->tpdate) == 0) {
335     fprintf(stderr,"failed to write .dbf field, TR #%d\n",TRCount);
336     exit(1);
337   }
338   TRCount++;
339 }
340 
transTRs()341 void transTRs()
342 { TPLIST curr, prev;
343 
344   if ((TRDateField=DBFAddField(DBFFile,"date",FTString,TRDATEWD,0)) == -1) {
345     fprintf(stderr,"failed to add .dbf field\n");
346     exit(1);
347   }
348   do {
349     if ((TR=curr=(TPLIST) malloc(sizeof(TPDATA))) == NULL) {
350       fprintf(stderr,"out of memory!");
351       exit(1);
352     }
353     TRlgth = 0;
354     do {
355       if (badTP(curr)) {
356 	fprintf(stderr,"invalid TP in TR: %s\n",Buffer);
357 	exit(1);
358       }
359       TRlgth++;
360       prev = curr;
361       if ((curr=(TPLIST) malloc(sizeof(TPDATA))) == NULL) {
362 	fprintf(stderr,"out of memory!");
363 	exit(1);
364       }
365       prev->tpnext = curr;
366     } while (getline(0) && Buffer[0]);
367     free(curr);
368     prev->tpnext = NULL;
369     saveTR();
370   } while (getline(1));
371 }
372 
main(int argc,char * argv[])373 int main(int argc, char *argv[])
374 {
375   if (argc != 2) {
376     fprintf(stderr,"Usage: gpstr2shp BASENAME\n");
377     exit(1);
378   }
379   if (badheader())  exit(1);
380   if (! getline(1)) {
381     fprintf(stderr,"no data found\n");
382     exit(1);
383   }
384   if ((DBFFile=DBFCreate(argv[1])) == NULL) {
385     fprintf(stderr,"cannot open %s for writing as .dbf\n",argv[1]);
386     exit(1);
387   }
388   switch (Buffer[0]) {
389   case 'W':
390     if ((SHPFile=SHPCreate(argv[1],WPTYPE)) == NULL) {
391       fprintf(stderr,"cannot open %s for writing\n",argv[1]);
392       exit(1);
393     }
394     transWPs();
395     break;
396   case 'R':
397     if ((SHPFile=SHPCreate(argv[1],RTTYPE)) == NULL) {
398       fprintf(stderr,"cannot open %s for writing\n",argv[1]);
399       exit(1);
400     }
401     transRTs();
402     break;
403   case 'T':
404     if ((SHPFile=SHPCreate(argv[1],TRTYPE)) == NULL) {
405       fprintf(stderr,"cannot open %s for writing\n",argv[1]);
406       exit(1);
407     }
408     transTRs();
409     break;
410   default: fprintf(stderr,"bad line: %s\n",Buffer);
411     exit(1);
412   }
413   SHPClose(SHPFile);  DBFClose(DBFFile);
414   exit(0);
415 }
416