1 /*---------------------------------------------------------------------------*\
2    gpsmanshp.c
3 
4    A layer for writing GPS data to Shapefile format files
5    using shapelib from Tcl
6 
7    This program was developed for use in
8    gpsman --- GPS Manager: a manager for GPS receiver data
9 
10    Copyright (c) 2003-2013 Miguel Filgueiras (migfilg@t-online.de)
11 
12     This program is free software; you can redistribute it and/or modify
13       it under the terms of the GNU General Public License as published by
14       the Free Software Foundation; either version 3 of the License, or
15       (at your option) any later version.
16 
17     This program is distributed in the hope that it will be useful,
18       but WITHOUT ANY WARRANTY; without even the implied warranty of
19       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20       GNU General Public License for more details.
21 
22       You should have received a copy of the GNU General Public License
23       along with this program.
24 
25 ------
26     This program uses
27     - shapelib, Copyright (c) 1999 by Frank Warmerdam <warmerda@home.com>
28 
29     and was based on
30     - gpstr2shp.c, Copyright (c) 2002 Miguel Filgueiras (mig@ncc.up.pt)
31     that translates files in GPStrans format to Shapefile
32 
33     This program provides the Tcl gpsmanshp package defining the following
34     Tcl commands:
35       GSHPOpenInputFiles BASEPATH
36       GSHPInfoFrom ID
37       GSHPGetObj ID INDEX
38       GSHPReadNextPoint ID
39       GSHPCreateFiles BASEPATH TYPE DIM
40       GSHPWriteWP ID X Y ?Z? NAME COMMENT DATE
41       GSHPCreateRT DIM RTID COMMENT
42       GSHPForgetRT
43       GSHPAddWPToRT X Y ?Z?
44       GSHPWriteRT ID FORGET
45       GSHPCreateTR DIM NAME COMMENT
46       GSHPForgetTR
47       GSHPAddTPToTR X Y ?Z?
48       GSHPWriteTR ID FORGET
49       GSHPCloseFiles ID
50 
51 -----
52 Revision history:
53 1.2.3   06-Oct-13
54          change of author email address
55 1.2.2   15-Jul-13
56          change of author email address
57 1.2.1   08-Jun-11
58          path to shapefil.h
59          change of author email address
60 1.2     05-Jul-04, reads .dbf fields for WP and UNKNOWN from files not written
61          by gpsmanshp
62 1.1     17-Nov-03, reads POLYGON/Z Shapes as UNKNOWN, and ARCM and POLYGONM as
63          UNKNOWN in 2 dimensions; reads/writes part (segment) information for
64 	 polylines (TR or UNKNOWN) and polygons (read only)
65 1.0.3   08-Nov-03, avoids warning in gcc with -Wall
66 1.0.2   07-Mar-03, avoids warnings in gcc with -Wall
67 1.0.1   27-Jun-02, avoids warnings in gcc
68 1.0     12-Jun-02
69 
70 \*---------------------------------------------------------------------------*/
71 
72 /* File: gpsmanshp.c *\
73 \* Last modified: 15 July 2013 */
74 
75 #include <stdlib.h>
76 #include <string.h>
77 #include <shapefil.h>
78 #include <tcl.h>
79 
80 #define VERSION "1.2.3"
81 
82 // #define DEBUG 1
83 
84 /* lengths of strings */
85 #define WPNAMEWD 50
86 #define WPCOMMTWD 128
87 #define WPDATEWD 25
88 #define RTIDWD 50
89 #define RTCOMMTWD 128
90 #define TRNAMEWD 50
91 #define TRCOMMTWD 128
92 #define MAXBUFFER 1024
93 
94 typedef struct wpstrt {
95   char wpname[WPNAMEWD], wpcommt[WPCOMMTWD], wpdate[WPDATEWD];
96   double wpx, wpy, wpz;
97   struct wpstrt *wpnext;
98 } WPDATA, *WPLIST;
99 
100 typedef struct {
101   char rtid[RTIDWD], rtcommt[RTCOMMTWD];
102   int rtdim;
103   double *rtxs, *rtys, *rtzs;
104   WPLIST rtwps;
105 } RTDATA;
106 
107 RTDATA RT;
108 int RTBuilding = 0, RTCount = 0, RTLgth;
109 WPLIST RTLastWP;
110 
111 typedef struct tpstrt {
112   double tpx, tpy, tpz;
113   struct tpstrt *tpnext;
114 } TPDATA, *TPLIST;
115 
116 typedef struct {
117   char trname[TRNAMEWD], trcommt[TRCOMMTWD];
118   int trdim, trnsegs, *trsegstarts, trsegsmax;
119   double *trxs, *trys, *trzs;
120   TPLIST trpts;
121 } TRDATA;
122 
123 TRDATA TR;
124 int TRBuilding = 0, TRCount = 0, TRLgth;
125 TPLIST TRLastTP;
126 
127 int RTRepr = 0, TRRepr = 0;
128 
129 #define WPTYPE3 SHPT_POINTZ
130 #define RTTYPE3 SHPT_ARCZ
131 #define TRTYPE3 SHPT_ARCZ
132 
133 #define WPTYPE2 SHPT_POINT
134 #define RTTYPE2 SHPT_ARC
135 #define TRTYPE2 SHPT_ARC
136 
137 typedef enum {WPs, RTs, TRs, UNKNOWN} GPSTYPE;
138 
139 /* the following 3 arrays must be kept aligned */
140 int SHPTypes[] = {SHPT_POINTZ, SHPT_ARCZ, SHPT_POINT, SHPT_ARC,
141                   SHPT_POLYGONZ, SHPT_POLYGON, SHPT_ARCM, SHPT_POLYGONM},
142   SHPTypeDim[] = {3, 3, 2, 2, 3, 2, 2, 2},
143   NSHPTypes = 8;
144 
145 GPSTYPE SHPGPSType[] = {WPs, UNKNOWN, WPs, UNKNOWN, UNKNOWN, UNKNOWN,
146                         UNKNOWN, UNKNOWN};
147 
148 /* to be indexed by GPSTYPE (not UNKNOWN) and 0,1 for 2 or 3 dimensions */
149 int SHPType[][2] = {{WPTYPE2, WPTYPE3}, {RTTYPE2, RTTYPE3},
150 		    {TRTYPE2, TRTYPE3}};
151 
152 /* max number of fields in gpsmanshp-generated .dbf files and in others*/
153 #define NFIELDS 3
154 #define MAXFIELDS 50
155 
156 typedef struct shpfset {
157   int id, settype, dim, input, field[NFIELDS], index;
158   GPSTYPE gpstype;
159   SHPHandle SHPFile;  DBFHandle DBFFile;
160   SHPObject *shpobj;
161   struct shpfset *nextset;
162 } SHPFILESET, *SHPFSETLIST;
163 
164 SHPFSETLIST FileSets = NULL;
165 int FileSetCount = 0;
166 
167 #define CHECKPARAMNO(number,mess) \
168    if (objc != number+1) { \
169     Tcl_WrongNumArgs(interp,1,objv,mess); \
170     return TCL_ERROR; \
171    }
172 #define CHECKPARAMNOS(min,max,mess) \
173    if (objc <= min || objc > max+1) { \
174     Tcl_WrongNumArgs(interp,1,objv,mess); \
175     return TCL_ERROR; \
176    }
177 #define GETINTPARAM(index,var) \
178    if (Tcl_GetIntFromObj(interp,objv[index],&var) != TCL_OK) { \
179      return TCL_ERROR; \
180    }
181 #define GETDOUBLEPARAM(index,var) \
182    if (Tcl_GetDoubleFromObj(interp,objv[index],&var) != TCL_OK) { \
183      return TCL_ERROR; \
184    }
185 #define GETSTRINGPARAM(index) Tcl_GetString(objv[index]);
186 #define RETURNINT(value) \
187    Tcl_SetObjResult(interp,Tcl_NewIntObj(value)); \
188    return TCL_OK;
189 #define RETURNLIST(number,vec) \
190    Tcl_SetObjResult(interp,Tcl_NewListObj(number,vec)); \
191     return TCL_OK;
192 
193 
cpstrclean(char * s,char * dest,int n)194 void cpstrclean(char *s, char *dest, int n)
195      /* copy string of length at most n, set to zero trailing chars */
196 {
197   while ((*dest++=*s++) && n--);
198   if (! n)  *--dest = 0;
199   else  while (n--) *dest++ = 0;
200 }
201 
findset(int id)202 SHPFSETLIST findset(int id)
203 { SHPFSETLIST p = FileSets;
204 
205   while (p != NULL) {
206     if (p->id == id)  return p;
207     p = p->nextset;
208   }
209   return NULL;
210 }
211 
nodbffields(SHPFSETLIST p)212 int nodbffields(SHPFSETLIST p)
213 { DBFHandle df = p->DBFFile;
214   // GSHPOpenInputFiles must be revised if there is any change in these fields
215   // the same is obviously true of the GSHPWrite... functions
216   switch (p->gpstype) {
217   case WPs:
218     return ((p->field[0]=DBFAddField(df,"name",FTString,WPNAMEWD,0)) == -1 ||
219 	    (p->field[1]=DBFAddField(df,"commt",FTString,WPCOMMTWD,0))
220 	    == -1 ||
221 	    (p->field[2]=DBFAddField(df,"date",FTString,WPDATEWD,0)) == -1);
222   case RTs:
223     return ((p->field[0]=DBFAddField(df,"id",FTString,RTIDWD,0)) == -1 ||
224 	    (p->field[1]=DBFAddField(df,"commt",FTString,RTCOMMTWD,0)) == -1);
225   case TRs:
226     return ((p->field[0]=DBFAddField(df,"name",FTString,TRNAMEWD,0)) == -1 ||
227 	    (p->field[1]=DBFAddField(df,"commt",FTString,TRCOMMTWD,0)) == -1);
228   default:
229     return 1;
230   }
231   return 1;
232 }
233 
getdbfotherfields(DBFHandle df,int n,int oix)234 Tcl_Obj *getdbfotherfields(DBFHandle df, int n, int oix)
235 { int i;
236   Tcl_Obj *ov[MAXFIELDS];
237 
238 #ifdef DEBUG
239   printf(">getdbfotherfields n=%d oix=%d\n",n,oix);
240 #endif
241 
242   if (df == NULL || n <= 0)  return NULL;
243   for (i=0; i<n; i++)
244       ov[i] = Tcl_NewStringObj(DBFReadStringAttribute(df,oix,i),-1);
245 
246 #ifdef DEBUG
247   printf(">returning from getdbfotherfields");
248 #endif
249 
250   return Tcl_NewListObj(n,ov);
251 }
252 
getdbffields(SHPFSETLIST p,int oix,Tcl_Obj * ov[],Tcl_Obj ** eflst)253 int getdbffields(SHPFSETLIST p, int oix, Tcl_Obj *ov[], Tcl_Obj **eflst)
254 { DBFHandle df = p->DBFFile;
255   int n = 2, i;
256 
257   if (p->gpstype == UNKNOWN) {
258     *eflst = getdbfotherfields(df,-p->field[0],oix);
259     return 0;
260   }
261   if (p->gpstype == WPs) {
262     *eflst = getdbfotherfields(df,-p->field[0],oix);
263     n = 3;
264   }
265   if (df == NULL)
266     for (i=0; i<n; i++)  ov[i] = Tcl_NewStringObj("",-1);
267   else
268     for (i=0; i<n; i++)
269       ov[i] = Tcl_NewStringObj(DBFReadStringAttribute(df,oix,p->field[i]),-1);
270   return n;
271 }
272 
GSHPCreateFiles(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])273 int GSHPCreateFiles(ClientData clientData,Tcl_Interp *interp,
274 		  int objc,Tcl_Obj *CONST objv[])
275      /* GSHPCreateFiles BASEPATH TYPE DIM */
276 { SHPFSETLIST p = FileSets, q;
277   int id, shptype, dim;
278   char *basename, *type;
279   SHPHandle sf;  DBFHandle df;
280   GPSTYPE gpstype;
281 
282 #ifdef DEBUG
283   printf(">GSHPCreateFiles, %d args\n",objc-1);
284 #endif
285 
286   CHECKPARAMNO(3,"BASEPATH TYPE DIM")
287   basename = GETSTRINGPARAM(1);  type = GETSTRINGPARAM(2);
288   GETINTPARAM(3,dim)
289   if (dim < 2 || dim > 3) {
290     RETURNINT(-2)
291   }
292   if (! strcmp(type,"WP"))  gpstype = WPs;
293   else if (! strcmp(type,"RT"))  gpstype = RTs;
294   else if (! strcmp(type,"TR"))  gpstype = TRs;
295   else {
296     RETURNINT(-1)
297   }
298   shptype = SHPType[gpstype][dim-2];
299   if ((df=DBFCreate(basename)) == NULL ||
300       (sf=SHPCreate(basename,shptype)) == NULL) {
301     RETURNINT(0)
302   }
303   if ((q=(SHPFSETLIST) malloc(sizeof(SHPFILESET))) == NULL) {
304     RETURNINT(-4)
305   }
306   if (p != NULL) {
307     while (p->nextset != NULL)  p = p->nextset;
308     p->nextset = q;
309   } else  FileSets = q;
310   id = q->id = ++FileSetCount;
311   q->settype = shptype;  q->dim = dim;  q->input = 0;
312   q->gpstype = gpstype;
313   q->SHPFile = sf;  q->DBFFile = df;  q->shpobj = NULL;
314   q->nextset = NULL;
315   if (nodbffields(q)) {
316     if (p != NULL)  p->nextset = NULL;
317     else  FileSets = NULL;
318     free(q);
319     RETURNINT(-3)
320   }
321   RETURNINT(id)
322 }
323 
GSHPOpenInputFiles(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])324 int GSHPOpenInputFiles(ClientData clientData,Tcl_Interp *interp,
325 		  int objc,Tcl_Obj *CONST objv[])
326      /* GSHPOpenInputFiles BASEPATH */
327 { SHPFSETLIST p = FileSets, q;
328   int id, shptype, dim, nents, i, f[NFIELDS], usefs;
329   char *basename;
330   SHPHandle sf;  DBFHandle df;
331   GPSTYPE gpstype;
332 
333 #ifdef DEBUG
334   printf(">GSHPOpenInputFiles, %d args\n",objc-1);
335 #endif
336 
337   CHECKPARAMNO(1,"BASEPATH")
338   basename = GETSTRINGPARAM(1);
339   if ((sf=SHPOpen(basename,"rb")) == NULL) {
340     RETURNINT(0)
341   }
342   SHPGetInfo(sf,&nents,&shptype,NULL,NULL);
343   if (nents == 0) {
344     RETURNINT(-1)
345   }
346   for (i=0; i<NSHPTypes; i++)
347     if (shptype == SHPTypes[i]) {
348       dim = SHPTypeDim[i];  gpstype = SHPGPSType[i];
349       break;
350     }
351   if (i == NSHPTypes) {
352     RETURNINT(-2)
353   }
354   if ((df=DBFOpen(basename,"rb")) != NULL) {
355     if (DBFGetRecordCount(df) == nents) {
356       usefs = 0;
357       switch ((i = DBFGetFieldCount(df))) {
358       case 3:
359 	if (gpstype != WPs ||
360 	    (f[0]=DBFGetFieldIndex(df,"name")) == -1 ||
361 	    (f[1]=DBFGetFieldIndex(df,"commt")) == -1 ||
362 	    (f[2]=DBFGetFieldIndex(df,"date")) == -1)
363 	  usefs = 1;
364 	break;
365       case 2:
366 	if ((f[1]=DBFGetFieldIndex(df,"commt")) == -1)
367 	  usefs = 1;
368 	else if ((f[0]=DBFGetFieldIndex(df,"id")) == -1)
369 	  if ((f[0]=DBFGetFieldIndex(df,"name")) == -1)  usefs = 1;
370 	  else  gpstype = TRs;
371 	else  gpstype = RTs;
372 	break;
373       case 0:
374 	df = NULL;
375       default:
376 	usefs = 1;
377       }
378       if (usefs) {
379 	if (i > MAXFIELDS)  f[0] = -MAXFIELDS;
380 	f[0] = -i;
381       }
382     } else  df = NULL;
383   }
384   if ((q=(SHPFSETLIST) malloc(sizeof(SHPFILESET))) == NULL) {
385     RETURNINT(-3)
386   }
387   if (p != NULL) {
388     while (p->nextset != NULL)  p = p->nextset;
389     p->nextset = q;
390   } else  FileSets = q;
391   id = q->id = ++FileSetCount;
392   q->settype = shptype;  q->dim = dim;
393   // this must be non-zero; otherwise the set can be taken as an output one
394   q->input = nents;  q->index = -1;
395   // gpstype may be UNKNOWN (an ARC/Z Shape without gpsmanshp written .dbf,
396   //  or a POLIGON/Z)
397   q->gpstype = gpstype;
398   q->SHPFile = sf;  q->DBFFile = df;
399   q->shpobj = NULL;
400   q->nextset = NULL;
401   for (i=0; i<NFIELDS; i++)  q->field[i] = f[i];
402 #ifdef DEBUG
403   printf("set %d:\n\tshptype=%d gpstype=%d dim=%d nents=%d dbf=%d usefs=%d\n",
404 	 id,shptype,gpstype,dim,nents,df!=NULL,usefs);
405 #endif
406   RETURNINT(id)
407 }
408 
GSHPInfoFrom(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])409 int GSHPInfoFrom(ClientData clientData,Tcl_Interp *interp,
410 		int objc,Tcl_Obj *CONST objv[])
411      /* GSHPInfoFrom ID */
412 { SHPFSETLIST p;
413   int id, n, i, k, j;
414   Tcl_Obj *ov[7], *fov[2*MAXFIELDS];
415   char buffer[MAXBUFFER];
416   DBFHandle df;
417 
418 #ifdef DEBUG
419   printf(">GSHPInfoFrom, %d args\n",objc-1);
420 #endif
421 
422   CHECKPARAMNO(1,"FILES_ID")
423   GETINTPARAM(1,id)
424   if ((p=findset(id)) == NULL || ! p->input) {
425     RETURNINT(0)
426   }
427   n = 4;
428   switch (p->gpstype) {
429   case WPs:  ov[0] = Tcl_NewStringObj("WP",-1);  n = 3;  break;
430   case RTs:  ov[0] = Tcl_NewStringObj("RT",-1);  break;
431   case TRs:  ov[0] = Tcl_NewStringObj("TR",-1);  break;
432   case UNKNOWN:  ov[0] = Tcl_NewStringObj("UNKNOWN",-1);
433   }
434   ov[1] = Tcl_NewIntObj(p->input);  ov[2] = Tcl_NewIntObj(p->dim);
435   if (n == 4)  ov[3] = Tcl_NewIntObj(p->index);
436   if ((df = p->DBFFile) == NULL) {
437     ov[n++] = Tcl_NewIntObj(0);
438     ov[n++] = Tcl_NewListObj(0,NULL);
439   } else if ((i = -p->field[0]) > 0) {
440     // get field names and precisions
441     for (k=j=0; k<i; k++) {
442       if (DBFGetFieldInfo(df,k,buffer,NULL,&id) == FTInvalid) {
443         *buffer = 0;  id = 0;
444       }
445       fov[j++] = Tcl_NewStringObj(buffer,-1);
446       fov[j++] = Tcl_NewIntObj(id);
447     }
448     ov[n++] = Tcl_NewIntObj(i);
449     ov[n++] = Tcl_NewListObj(j,fov);
450   }
451   RETURNLIST(n,ov)
452 }
453 
GSHPGetObj(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])454 int GSHPGetObj(ClientData clientData,Tcl_Interp *interp,
455 		  int objc,Tcl_Obj *CONST objv[])
456      /* GSHPGetObj ID INDEX */
457 { SHPFSETLIST p;
458   int id, oix, n, nsegs, *segstart, i, k;
459   Tcl_Obj *ov[6], *eflst, **ppov, **ppovnxt;
460 
461 #ifdef DEBUG
462   printf(">GSHPGetObj, %d args\n",objc-1);
463 #endif
464 
465   CHECKPARAMNO(2,"FILES_ID INDEX")
466   GETINTPARAM(1,id)  GETINTPARAM(2,oix)
467   if ((p=findset(id)) == NULL || ! p->input) {
468     RETURNINT(-1)
469   }
470   p->index = -1;
471   if (p->shpobj != NULL) {
472     SHPDestroyObject(p->shpobj);
473     p->shpobj = NULL;
474   }
475   if (oix < 0 || oix >= p->input ||
476       (p->shpobj=SHPReadObject(p->SHPFile,oix)) == NULL) {
477     RETURNINT(-2)
478   }
479   if (p->shpobj->nSHPType == SHPT_NULL) {
480     SHPDestroyObject(p->shpobj);
481     p->shpobj = NULL;
482     RETURNLIST(0,NULL)
483   }
484   n = 0;
485   switch (p->gpstype) {
486   case WPs:
487     n = getdbffields(p,oix,ov,&eflst);
488     ov[n++] = Tcl_NewDoubleObj(p->shpobj->padfX[0]);
489     ov[n++] = Tcl_NewDoubleObj(p->shpobj->padfY[0]);
490     if (p->dim == 3)
491       ov[n++] = Tcl_NewDoubleObj(p->shpobj->padfZ[0]);
492     if (eflst != NULL)  ov[n++] = eflst;
493     break;
494   case RTs:
495     n = getdbffields(p,oix,ov,NULL);
496     ov[n++] = Tcl_NewIntObj(p->shpobj->nVertices);
497     p->index = 0;
498     break;
499   case TRs:
500     n = getdbffields(p,oix,ov,NULL);
501   case UNKNOWN:
502     ov[n++] = Tcl_NewIntObj(p->shpobj->nVertices);
503     if ((nsegs=p->shpobj->nParts) != 0) {
504       if ((ppov = (Tcl_Obj **) malloc(nsegs*sizeof(Tcl_Obj *))) == NULL) {
505 	RETURNINT(-3)
506       }
507       segstart = p->shpobj->panPartStart;
508       ppovnxt = ppov;  i = 0;
509       do
510 	if ((k = (*segstart++)) > 0) {
511 	  *ppovnxt++ = Tcl_NewIntObj(k);
512 #ifdef DEBUG
513 	  printf(">GSHPGetObj, segment starter: %d\n",i);  fflush(stdout);
514 #endif
515 	  i++;
516 	}
517       while (--nsegs);
518       if (i != 0) {
519 	ov[n++] = Tcl_NewListObj(i,ppov);
520       }
521 
522 #ifdef DEBUG
523       printf(">GSHPGetObj, about freeing memory\n");  fflush(stdout);
524 #endif
525 
526       free(ppov);
527     }
528     if (p->gpstype == UNKNOWN) {
529       getdbffields(p,oix,NULL,&eflst);
530       if (eflst != NULL) {
531 	if (n == 1)  ov[n++] = Tcl_NewListObj(0,NULL);
532 	ov[n++] = eflst;
533       }
534     }
535     p->index = 0;
536   }
537   RETURNLIST(n,ov)
538 }
539 
GSHPReadNextPoint(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])540 int GSHPReadNextPoint(ClientData clientData,Tcl_Interp *interp,
541 		  int objc,Tcl_Obj *CONST objv[])
542      /* GSHPReadNextPoint ID */
543 { SHPFSETLIST p;
544   int id, eix, n = 2;
545   Tcl_Obj *ov[3];
546 
547 #ifdef DEBUG
548   printf(">GSHPReadNextPoint, %d args\n",objc-1);
549 #endif
550 
551   CHECKPARAMNO(1,"FILES_ID")
552   GETINTPARAM(1,id)
553   if ((p=findset(id)) == NULL || ! p->input) {
554     RETURNINT(0)
555   }
556   if ((eix=p->index) < 0) {
557     RETURNINT(-1)
558   }
559   if (eix == p->shpobj->nVertices) {
560     p->index = -1;
561     SHPDestroyObject(p->shpobj);
562     p->shpobj = NULL;
563     RETURNINT(-2)
564   }
565   ov[0] = Tcl_NewDoubleObj(p->shpobj->padfX[eix]);
566   ov[1] = Tcl_NewDoubleObj(p->shpobj->padfY[eix]);
567   if (p->dim == 3)
568     ov[n++] = Tcl_NewDoubleObj(p->shpobj->padfZ[eix]);
569   p->index++;
570   RETURNLIST(n,ov)
571 }
572 
GSHPCloseFiles(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])573 int GSHPCloseFiles(ClientData clientData,Tcl_Interp *interp,
574 		  int objc,Tcl_Obj *CONST objv[])
575      /* GSHPCloseFiles ID */
576 { SHPFSETLIST p = FileSets, q = NULL;
577   int id;
578 
579 #ifdef DEBUG
580   printf(">GSHPCloseFiles, %d args\n",objc-1);
581 #endif
582 
583   CHECKPARAMNO(1,"FILES_ID")
584   GETINTPARAM(1,id)
585   while (p != NULL && p->id != id) {
586     q = p; p = p->nextset;
587   }
588   if (p == NULL) {
589     RETURNINT(0)
590   }
591   SHPClose(p->SHPFile);
592   if (p->DBFFile != NULL)  DBFClose(p->DBFFile);
593   if (p->shpobj != NULL)  SHPDestroyObject(p->shpobj);
594   if (q != NULL)  q->nextset = p->nextset;
595   else  FileSets = p->nextset;
596   free(p);
597   RETURNINT(1)
598 }
599 
GSHPWriteWP(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])600 int GSHPWriteWP(ClientData clientData,Tcl_Interp *interp,
601 		  int objc,Tcl_Obj *CONST objv[])
602      /* GSHPWriteWP ID X Y ?Z? NAME COMMENT DATE */
603 { SHPFSETLIST p;
604   int id, entno, dim;
605   double x, y, z;
606   char *name, *commt, *date;
607   SHPObject *pwpo;
608   DBFHandle df;
609 
610 #ifdef DEBUG
611   printf(">GSHPWriteWP, %d args\n",objc-1);
612 #endif
613 
614   CHECKPARAMNOS(6,7,"FILES_ID X Y ?Z? NAME COMMENT DATE")
615   dim = objc-5;
616   GETINTPARAM(1,id)
617   GETDOUBLEPARAM(2,x)    GETDOUBLEPARAM(3,y)
618   if (dim == 3) {
619     GETDOUBLEPARAM(4,z)
620     name = GETSTRINGPARAM(5)    commt = GETSTRINGPARAM(6)
621     date = GETSTRINGPARAM(7)
622   } else {
623     z = 0;
624     name = GETSTRINGPARAM(4)    commt = GETSTRINGPARAM(5)
625     date = GETSTRINGPARAM(6)
626   }
627   if ((p=findset(id)) == NULL || p->input) {
628     RETURNINT(-1)
629   }
630   if  (p->settype != SHPType[WPs][dim-2]) {
631     RETURNINT(-2)
632   }
633 
634 #ifdef DEBUG
635   if (dim == 3)
636     printf("W\t%s\t%s\t%s\t%lf\t%lf\t%lf\n",name,commt,date,x,y,z);
637   else
638     printf("W\t%s\t%s\t%s\t%lf\t%lf\n",name,commt,date,x,y);
639 #endif
640 
641   if ((pwpo=SHPCreateSimpleObject(p->settype,1,&x,&y,&z)) == NULL) {
642     RETURNINT(-3)
643   }
644   entno = SHPWriteObject(p->SHPFile,-1,pwpo);
645   SHPDestroyObject(pwpo);
646   df = p->DBFFile;
647   if (DBFWriteStringAttribute(df,entno,p->field[0],name) == 0 ||
648       DBFWriteStringAttribute(df,entno,p->field[1],commt) == 0 ||
649       DBFWriteStringAttribute(df,entno,p->field[2],date) == 0) {
650     RETURNINT(-4)
651   }
652   RETURNINT(1)
653 }
654 
GSHPCreateRT(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])655 int GSHPCreateRT(ClientData clientData,Tcl_Interp *interp,
656 		  int objc,Tcl_Obj *CONST objv[])
657      /* GSHPCreateRT DIM RTID COMMENT */
658 { char *name, *commt;
659   int dim;
660 
661 #ifdef DEBUG
662   printf(">GSHPCreateRT, %d args\n",objc-1);
663 #endif
664 
665   CHECKPARAMNO(3,"DIM RTID COMMENT")
666   GETINTPARAM(1,dim)
667   if (dim < 2 || dim > 3) {
668     RETURNINT(-1)
669   }
670   name = GETSTRINGPARAM(2)    commt = GETSTRINGPARAM(3)
671   if (RTBuilding) {
672     RETURNINT(0)
673   }
674   RTBuilding = 1;
675   cpstrclean(name,RT.rtid,RTIDWD);
676   cpstrclean(commt,RT.rtcommt,RTCOMMTWD);
677   RT.rtwps = NULL;  RT.rtxs = NULL;  RT.rtys = NULL;  RT.rtzs = NULL;
678   RT.rtdim = dim;
679   RTLgth = 0;
680   RETURNINT(1)
681 }
682 
forgetRT()683 void forgetRT()
684 { WPLIST p, q;
685 
686   RTBuilding = 0;
687   p = RT.rtwps;
688   while (p != NULL) {
689     q = p;  p = p->wpnext;  free(q);
690   }
691   if (RT.rtxs != NULL) {
692     free(RT.rtxs);    free(RT.rtys);    free(RT.rtzs);
693   }
694 }
695 
GSHPForgetRT(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])696 int GSHPForgetRT(ClientData clientData,Tcl_Interp *interp,
697 		  int objc,Tcl_Obj *CONST objv[])
698      /* GSHPForgetRT */
699 {
700 #ifdef DEBUG
701   printf(">GSHPForgetRT, %d args\n",objc-1);
702 #endif
703 
704   CHECKPARAMNO(0,NULL)
705   if (! RTBuilding) {
706     RETURNINT(0)
707   }
708   forgetRT();
709   RETURNINT(1)
710 }
711 
GSHPAddWPToRT(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])712 int GSHPAddWPToRT(ClientData clientData,Tcl_Interp *interp,
713 		  int objc,Tcl_Obj *CONST objv[])
714      /* GSHPAddWPToRT X Y ?Z? */
715 { double x, y, z;
716   WPLIST wpp;
717   int dim;
718 
719 #ifdef DEBUG
720   printf(">GSHPAddWPToRT, %d args\n",objc-1);
721 #endif
722 
723   CHECKPARAMNOS(2,3,"X Y ?Z?")
724   dim = objc-1;
725   GETDOUBLEPARAM(1,x)    GETDOUBLEPARAM(2,y)
726   if (dim == 3) {
727     GETDOUBLEPARAM(3,z)
728   } else  z = 0;
729   if (! RTBuilding || dim != RT.rtdim) {
730     RETURNINT(-1)
731   }
732   if ((wpp=(WPLIST) malloc(sizeof(WPDATA))) == NULL) {
733     RETURNINT(-2)
734   }
735   wpp->wpx = x;  wpp->wpy = y;  wpp->wpz = z;
736   wpp->wpnext = NULL;
737   if (RTLgth++)  RTLastWP->wpnext = wpp;
738   else  RT.rtwps = wpp;
739   if (RT.rtxs != NULL) {
740     free(RT.rtxs);    free(RT.rtys);    free(RT.rtzs);
741     RT.rtxs = NULL;
742   }
743   RTLastWP = wpp;
744   RETURNINT(1)
745 }
746 
GSHPWriteRT(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])747 int GSHPWriteRT(ClientData clientData,Tcl_Interp *interp,
748 		  int objc,Tcl_Obj *CONST objv[])
749      /* GSHPWriteRT ID FORGET */
750 { SHPFSETLIST p;
751   int id, forget, entno, i, dim;
752   WPLIST wpp;
753   SHPObject *prto;
754   DBFHandle df;
755 
756 #ifdef DEBUG
757   printf(">GSHPWriteRT, %d args\n",objc-1);
758 #endif
759 
760   CHECKPARAMNO(2,"FILES_ID FORGET")
761   GETINTPARAM(1,id)  GETINTPARAM(2,forget)
762   if (! RTBuilding) {
763     RETURNINT(-1)
764   }
765   if (RTLgth == 0) {
766     RETURNINT(-2)
767   }
768   if ((p=findset(id)) == NULL || p->input) {
769     RETURNINT(-3)
770   }
771   dim = RT.rtdim;
772   if  (p->settype != SHPType[RTs][dim-2]) {
773     RETURNINT(-4)
774   }
775 
776 #ifdef DEBUG
777   printf("R\t%s\t%s\n",RT.rtid,RT.rtcommt);
778   wpp = RT.rtwps;
779   while (wpp != NULL) {
780     if (dim == 2)
781       printf("W\t\t\t\t%lf\t%lf\n",wpp->wpx,wpp->wpy);
782     else  printf("W\t\t\t\t%lf\t%lf\t%lf\n",wpp->wpx,wpp->wpy,wpp->wpz);
783     wpp = wpp->wpnext;
784   }
785 #endif
786 
787   if (RT.rtxs == NULL) {
788     if ((RT.rtxs=(double *) malloc(RTLgth*sizeof(double))) == NULL) {
789       RETURNINT(-5)
790     }
791     if ((RT.rtys=(double *) malloc(RTLgth*sizeof(double))) == NULL ||
792 	(dim == 3  &&
793 	 (RT.rtzs=(double *) malloc(RTLgth*sizeof(double))) == NULL)) {
794       free(RT.rtxs);  free(RT.rtys);
795       RT.rtxs = NULL;
796       RETURNINT(-5)
797     }
798     wpp = RT.rtwps;
799     for(i=0; wpp != NULL; i++) {
800       RT.rtxs[i] = wpp->wpx;  RT.rtys[i] = wpp->wpy;
801       if (dim == 3)  RT.rtzs[i] = wpp->wpz;
802       wpp = wpp->wpnext;
803     }
804   }
805   if ((prto=SHPCreateObject(p->settype,RTCount,0,NULL,NULL,RTLgth,
806 			    RT.rtxs,RT.rtys,RT.rtzs,NULL)) == NULL) {
807     RETURNINT(-5)
808   }
809   entno = SHPWriteObject(p->SHPFile,-1,prto);
810   SHPDestroyObject(prto);
811   RTCount++;
812 
813   df = p->DBFFile;
814   if (DBFWriteStringAttribute(df,entno,p->field[0],RT.rtid) == 0 ||
815       DBFWriteStringAttribute(df,entno,p->field[1],RT.rtcommt) == 0) {
816     RETURNINT(-6)
817   }
818   if (forget)  forgetRT();
819   RETURNINT(1)
820 }
821 
GSHPCreateTR(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])822 int GSHPCreateTR(ClientData clientData,Tcl_Interp *interp,
823 		  int objc,Tcl_Obj *CONST objv[])
824      /* GSHPCreateTR DIM NAME COMMENT */
825 { char *name, *commt;
826   int dim, nsegs, *segstarts, *segnxt, i, k, kp;
827   Tcl_Obj **objvPtr;
828 
829 #ifdef DEBUG
830   printf(">GSHPCreateTR, %d args\n",objc-1);
831 #endif
832 
833   CHECKPARAMNOS(3,4,"DIM NAME COMMENT ?SEGSTARTERS?")
834   GETINTPARAM(1,dim)
835   if (dim < 2 || dim > 3) {
836     RETURNINT(-1)
837   }
838   if (TRBuilding) {
839     RETURNINT(0)
840   }
841   name = GETSTRINGPARAM(2)    commt = GETSTRINGPARAM(3)
842   kp = 0;
843   if (objc-1 == 3) {
844     nsegs = 0;  segstarts = NULL;
845   } else {
846     if (Tcl_ListObjGetElements(interp,objv[4],&nsegs,&objvPtr) != TCL_OK)
847       return TCL_ERROR;
848 #ifdef DEBUG
849     printf(">GSHPCreateTR, %d segs\n",nsegs);
850 #endif
851     if (nsegs == 0) {
852       segstarts = NULL;
853     } else {
854       if ((segstarts = (int *) malloc(++nsegs*sizeof(int))) == NULL) {
855 	RETURNINT(-2)
856       }
857 #ifdef DEBUG
858       printf(">GSHPCreateTR, memory allocated for %d segs\n",nsegs);
859 #endif
860       segnxt = segstarts;
861       // make sure 0 is the first one
862       *segnxt++ = 0;
863       for (i=1; i<nsegs; i++) {
864 	if (Tcl_GetIntFromObj(interp,*objvPtr,&k) != TCL_OK || k <= kp) {
865 	  free(segstarts);
866 	  RETURNINT(-3)
867         }
868 #ifdef DEBUG
869 	printf(">GSHPCreateTR, adding %d seg start\n",k);
870 #endif
871 	objvPtr++;
872 	*segnxt++ = kp = k;
873       }
874     }
875   }
876   TRBuilding = 1;
877   cpstrclean(name,TR.trname,TRNAMEWD);
878   cpstrclean(commt,TR.trcommt,TRCOMMTWD);
879   TR.trnsegs = nsegs;  TR.trsegstarts = segstarts;  TR.trsegsmax = kp;
880   TR.trpts = NULL;  TR.trxs = NULL;  TR.trys = NULL;  TR.trzs = NULL;
881   TR.trdim = dim;
882   TRLgth = 0;
883   RETURNINT(1)
884 }
885 
forgetTR()886 void forgetTR()
887 { TPLIST p, q;
888 
889   TRBuilding = 0;
890   p = TR.trpts;
891   while (p != NULL) {
892     q = p;  p = p->tpnext;  free(q);
893   }
894   if (TR.trnsegs != 0)  free(TR.trsegstarts);
895   if (TR.trxs != NULL) {
896     free(TR.trxs);    free(TR.trys);    free(TR.trzs);
897   }
898 }
899 
GSHPForgetTR(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])900 int GSHPForgetTR(ClientData clientData,Tcl_Interp *interp,
901 		  int objc,Tcl_Obj *CONST objv[])
902      /* GSHPForgetTR */
903 {
904 #ifdef DEBUG
905   printf(">GSHPForgetTR, %d args\n",objc-1);
906 #endif
907 
908   CHECKPARAMNO(0,NULL)
909   if (! TRBuilding) {
910     RETURNINT(0)
911   }
912   forgetTR();
913   RETURNINT(1)
914 }
915 
GSHPAddTPToTR(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])916 int GSHPAddTPToTR(ClientData clientData,Tcl_Interp *interp,
917 		  int objc,Tcl_Obj *CONST objv[])
918      /* GSHPAddTPToTR X Y ?Z? */
919 { double x, y, z;
920   TPLIST tpp;
921   int dim;
922 
923 #ifdef DEBUG
924   printf(">GSHPAddTPToTR, %d args\n",objc-1);
925 #endif
926 
927   CHECKPARAMNOS(2,3,"X Y ?Z?")
928   dim = objc-1;
929   GETDOUBLEPARAM(1,x)    GETDOUBLEPARAM(2,y)
930   if (dim == 3) {
931     GETDOUBLEPARAM(3,z)
932   } else  z = 0;
933   if (! TRBuilding || TR.trdim != dim) {
934     RETURNINT(-1)
935   }
936   if ((tpp=(TPLIST) malloc(sizeof(TPDATA))) == NULL) {
937     RETURNINT(-2)
938   }
939   tpp->tpx = x;  tpp->tpy = y;  tpp->tpz = z;
940   tpp->tpnext = NULL;
941   if (TRLgth++)  TRLastTP->tpnext = tpp;
942   else  TR.trpts = tpp;
943   if (TR.trxs != NULL) {
944     free(TR.trxs);    free(TR.trys);    free(TR.trzs);
945     TR.trxs = NULL;
946   }
947   TRLastTP = tpp;
948   RETURNINT(1)
949 }
950 
GSHPWriteTR(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])951 int GSHPWriteTR(ClientData clientData,Tcl_Interp *interp,
952 		  int objc,Tcl_Obj *CONST objv[])
953      /* GSHPWriteTR ID FORGET */
954 { SHPFSETLIST p;
955  int id, forget, entno, i, dim;
956   TPLIST tpp;
957   SHPObject *ptro;
958   DBFHandle df;
959 
960 #ifdef DEBUG
961   int *segs, nsegs, k;
962   printf(">GSHPWriteTR, %d args\n",objc-1);
963 #endif
964 
965   CHECKPARAMNO(2,"FILES_ID FORGET")
966   GETINTPARAM(1,id)  GETINTPARAM(2,forget)
967   if (! TRBuilding) {
968     RETURNINT(-1)
969   }
970   if (TRLgth == 0) {
971     RETURNINT(-2)
972   }
973   if (TRLgth-1 < TR.trsegsmax) {
974     RETURNINT(-7)
975   }
976   if ((p=findset(id)) == NULL || p->input) {
977     RETURNINT(-3)
978   }
979   dim = TR.trdim;
980   if  (p->settype != SHPType[TRs][dim-2]) {
981     RETURNINT(-4)
982   }
983 
984 #ifdef DEBUG
985   printf("TR\t%s\t%s\t%d\n",TR.trname,TR.trcommt,TR.trnsegs);
986   tpp = TR.trpts;  i = 0;
987   if ((nsegs = TR.trnsegs) == 0)
988     k = -1;
989   else {
990     segs = TR.trsegstarts;
991     k = *segs++;
992   }
993   while (tpp != NULL) {
994     if (dim == 3)
995       printf("T\t\t%lf\t%lf\t%lf",tpp->tpx,tpp->tpy,tpp->tpz);
996     else  printf("T\t\t%lf\t%lf",tpp->tpx,tpp->tpy);
997     tpp = tpp->tpnext;
998     if (i++ == k) {
999       printf("\t; segment starter\n");
1000       if (--nsegs == 0)
1001 	k = -1;
1002       else  k = *segs++;
1003     } else  putchar('\n');
1004   }
1005 #endif
1006 
1007   if (TR.trxs == NULL) {
1008     if ((TR.trxs=(double *) malloc(TRLgth*sizeof(double))) == NULL) {
1009       RETURNINT(-5)
1010     }
1011     if ((TR.trys=(double *) malloc(TRLgth*sizeof(double))) == NULL ||
1012 	(dim == 3 &&
1013 	 (TR.trzs=(double *) malloc(TRLgth*sizeof(double))) == NULL)) {
1014       free(TR.trxs);  free(TR.trys);
1015       TR.trxs = NULL;
1016       RETURNINT(-5)
1017     }
1018     tpp = TR.trpts;
1019     for(i=0; tpp != NULL; i++) {
1020       TR.trxs[i] = tpp->tpx;  TR.trys[i] = tpp->tpy;
1021       if (dim == 3)  TR.trzs[i] = tpp->tpz;
1022       tpp = tpp->tpnext;
1023     }
1024   }
1025   if ((ptro=SHPCreateObject(p->settype,TRCount,TR.trnsegs,TR.trsegstarts,NULL,
1026 			    TRLgth,TR.trxs,TR.trys,TR.trzs,NULL)) == NULL) {
1027     RETURNINT(-5)
1028   }
1029   entno = SHPWriteObject(p->SHPFile,-1,ptro);
1030   SHPDestroyObject(ptro);
1031   TRCount++;
1032 
1033   df = p->DBFFile;
1034   if (DBFWriteStringAttribute(df,entno,p->field[0],TR.trname) == 0 ||
1035       DBFWriteStringAttribute(df,entno,p->field[1],TR.trcommt) == 0) {
1036     RETURNINT(-6)
1037   }
1038   if (forget)  forgetTR();
1039   RETURNINT(1)
1040 }
1041 
1042 /* Tcl interface */
1043 
1044 int Gpsmanshp_Init(Tcl_Interp *interp);
1045 
Tclgpsmanshp_Init(Tcl_Interp * interp)1046 int Tclgpsmanshp_Init(Tcl_Interp *interp)
1047 {
1048   return Gpsmanshp_Init(interp);
1049 }
1050 
Gpsmanshp_Init(Tcl_Interp * interp)1051 int Gpsmanshp_Init(Tcl_Interp *interp)
1052 {
1053   Tcl_CreateObjCommand(interp,"GSHPCreateFiles",GSHPCreateFiles,
1054 		       (ClientData)NULL,NULL);
1055   Tcl_CreateObjCommand(interp,"GSHPOpenInputFiles",GSHPOpenInputFiles,
1056 		       (ClientData)NULL,NULL);
1057   Tcl_CreateObjCommand(interp,"GSHPInfoFrom",GSHPInfoFrom,
1058 		       (ClientData)NULL,NULL);
1059   Tcl_CreateObjCommand(interp,"GSHPGetObj",GSHPGetObj,
1060 		       (ClientData)NULL,NULL);
1061   Tcl_CreateObjCommand(interp,"GSHPReadNextPoint",GSHPReadNextPoint,
1062 		       (ClientData)NULL,NULL);
1063   Tcl_CreateObjCommand(interp,"GSHPCloseFiles",GSHPCloseFiles,
1064 		       (ClientData)NULL,NULL);
1065   Tcl_CreateObjCommand(interp,"GSHPWriteWP",GSHPWriteWP,
1066 		       (ClientData)NULL,NULL);
1067   Tcl_CreateObjCommand(interp,"GSHPCreateRT",GSHPCreateRT,
1068 		       (ClientData)NULL,NULL);
1069   Tcl_CreateObjCommand(interp,"GSHPForgetRT",GSHPForgetRT,
1070 		       (ClientData)NULL,NULL);
1071   Tcl_CreateObjCommand(interp,"GSHPAddWPToRT",GSHPAddWPToRT,
1072 		       (ClientData)NULL,NULL);
1073   Tcl_CreateObjCommand(interp,"GSHPWriteRT",GSHPWriteRT,(ClientData)NULL,NULL);
1074   Tcl_CreateObjCommand(interp,"GSHPCreateTR",GSHPCreateTR,
1075 		       (ClientData)NULL,NULL);
1076   Tcl_CreateObjCommand(interp,"GSHPForgetTR",GSHPForgetTR,
1077 		       (ClientData)NULL,NULL);
1078   Tcl_CreateObjCommand(interp,"GSHPAddTPToTR",GSHPAddTPToTR,
1079 		       (ClientData)NULL,NULL);
1080   Tcl_CreateObjCommand(interp,"GSHPWriteTR",GSHPWriteTR,(ClientData)NULL,NULL);
1081 
1082   Tcl_PkgProvide(interp,"gpsmanshp",VERSION);
1083   return TCL_OK;
1084 }
1085 
1086