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