PageRenderTime 194ms CodeModel.GetById 60ms app.highlight 95ms RepoModel.GetById 26ms app.codeStats 0ms

/src/main/C/shapelib-1.2.10/shputils.c

https://github.com/SumanthMN/census_utils
C | 1050 lines | 712 code | 90 blank | 248 comment | 260 complexity | 7b3a0fa6160cd46921fb209cb14cba68 MD5 | raw file
   1/******************************************************************************
   2 * $Id: shputils.c,v 1.7 2003/02/25 17:20:22 warmerda Exp $
   3 *
   4 * Project:  Shapelib
   5 * Purpose:  
   6 *   Altered "shpdump" and "dbfdump" to allow two files to be appended.
   7 *   Other Functions:
   8 *     Selecting from the DBF before the write occurs.
   9 *     Change the UNITS between Feet and Meters and Shift X,Y.
  10 *     Clip and Erase boundary.  The program only passes thru the
  11 *     data once.
  12 *
  13 *   Bill Miller   North Carolina - Department of Transporation 
  14 *   Feb. 1997 -- bmiller@dot.state.nc.us
  15 *         There was not a lot of time to debug hidden problems;
  16 *         And the code is not very well organized or documented.
  17 *         The clip/erase function was not well tested.
  18 *   Oct. 2000 -- bmiller@dot.state.nc.us
  19 *         Fixed the problem when select is using numbers
  20 *         larger than short integer.  It now reads long integer.
  21 *   NOTE: DBF files created using windows NT will read as a string with
  22 *         a length of 381 characters.  This is a bug in "dbfopen".
  23 *
  24 *
  25 * Author:   Bill Miller (bmiller@dot.state.nc.us)
  26 *
  27 ******************************************************************************
  28 * Copyright (c) 1999, Frank Warmerdam
  29 *
  30 * This software is available under the following "MIT Style" license,
  31 * or at the option of the licensee under the LGPL (see LICENSE.LGPL).  This
  32 * option is discussed in more detail in shapelib.html.
  33 *
  34 * --
  35 * 
  36 * Permission is hereby granted, free of charge, to any person obtaining a
  37 * copy of this software and associated documentation files (the "Software"),
  38 * to deal in the Software without restriction, including without limitation
  39 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  40 * and/or sell copies of the Software, and to permit persons to whom the
  41 * Software is furnished to do so, subject to the following conditions:
  42 *
  43 * The above copyright notice and this permission notice shall be included
  44 * in all copies or substantial portions of the Software.
  45 *
  46 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  47 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  48 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  49 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  50 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  51 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  52 * DEALINGS IN THE SOFTWARE.
  53 ******************************************************************************
  54 *
  55 * $Log: shputils.c,v $
  56 * Revision 1.7  2003/02/25 17:20:22  warmerda
  57 * Set psCShape to NULL after SHPDestroyObject() to avoid multi-frees of
  58 * the same memory ... as submitted by Fred Fox.
  59 *
  60 * Revision 1.6  2001/08/28 13:57:14  warmerda
  61 * fixed DBFAddField return value check
  62 *
  63 * Revision 1.5  2000/11/02 13:52:48  warmerda
  64 * major upgrade from Bill Miller
  65 *
  66 * Revision 1.4  1999/11/05 14:12:05  warmerda
  67 * updated license terms
  68 *
  69 * Revision 1.3  1998/12/03 15:47:39  warmerda
  70 * Did a bunch of rewriting to make it work with the V1.2 API.
  71 *
  72 * Revision 1.2  1998/06/18 01:19:49  warmerda
  73 * Made C++ compilable.
  74 *
  75 * Revision 1.1  1997/05/27 20:40:27  warmerda
  76 * Initial revision
  77 */
  78
  79static char rcsid[] = 
  80  "$Id: shputils.c,v 1.7 2003/02/25 17:20:22 warmerda Exp $";
  81
  82#include "shapefil.h"
  83#include "string.h"
  84#ifndef FALSE
  85#  define FALSE		0
  86#  define TRUE		1
  87#endif
  88
  89char            infile[80], outfile[80], temp[400];
  90
  91/* Variables for shape files */
  92SHPHandle	hSHP;
  93SHPHandle	hSHPappend;
  94int		nShapeType, nEntities, iPart;
  95int		nShapeTypeAppend, nEntitiesAppend;
  96SHPObject	*psCShape;
  97double		adfBoundsMin[4], adfBoundsMax[4];
  98
  99
 100/* Variables for DBF files */
 101DBFHandle	hDBF;
 102DBFHandle	hDBFappend;
 103    
 104DBFFieldType    iType;
 105DBFFieldType    jType;
 106    
 107char	iszTitle[12];
 108char	jszTitle[12];
 109
 110int	*pt;
 111char	iszFormat[32], iszField[1024];
 112char	jszFormat[32], jszField[1024];
 113int	i, ti, iWidth, iDecimals, iRecord;
 114int	j, tj, jWidth, jDecimals, jRecord;
 115int     found, newdbf;
 116
 117
 118void openfiles(void);
 119void setext(char *pt, char *ext);
 120int strncasecmp2(char *s1, char *s2, int n);
 121void mergefields(void);
 122void findselect(void);
 123void showitems(void);
 124int selectrec();
 125int check_theme_bnd();
 126int clip_boundary();
 127void error();
 128
 129
 130/* -------------------------------------------------------------------- */
 131/* Variables for the DESCRIBE function */
 132/* -------------------------------------------------------------------- */
 133   int       ilist = FALSE, iall = FALSE;
 134/* -------------------------------------------------------------------- */
 135/* Variables for the SELECT function */
 136/* -------------------------------------------------------------------- */
 137   int       found = FALSE, newdbf = FALSE;
 138   char      selectitem[40], *cpt;
 139   long int  selectvalues[150], selcount=0;
 140   int       iselect = FALSE, iselectitem = -1;
 141   int       iunselect = FALSE;
 142
 143/* -------------------------------------------------------------------- */
 144/* Variables for the CLIP and ERASE functions */
 145/* -------------------------------------------------------------------- */
 146   double  cxmin, cymin, cxmax, cymax; 
 147   int     iclip  = FALSE, ierase = FALSE;
 148   int     itouch = FALSE, iinside = FALSE, icut = FALSE;
 149   int     ibound = FALSE, ipoly = FALSE;
 150   char    clipfile[80];
 151
 152/* -------------------------------------------------------------------- */
 153/* Variables for the FACTOR function */
 154/* -------------------------------------------------------------------- */
 155   double  infactor,outfactor,factor = 0;  /* NO FACTOR */
 156   int     iunit = FALSE;
 157   int     ifactor = FALSE;
 158
 159   
 160/* -------------------------------------------------------------------- */
 161/* Variables for the SHIFT function */
 162/* -------------------------------------------------------------------- */
 163   double  xshift = 0, yshift = 0;  /* NO SHIFT */
 164      
 165int main( int argc, char ** argv )
 166{
 167
 168/* -------------------------------------------------------------------- */
 169/*      Check command line usage.                                       */
 170/* -------------------------------------------------------------------- */
 171    if( argc < 2 ) error();
 172    strcpy(infile, argv[1]);
 173    if (argc > 2) {
 174        strcpy(outfile,argv[2]);
 175        if (strncasecmp2(outfile, "LIST",0) == 0) { ilist = TRUE; }
 176        if (strncasecmp2(outfile, "ALL",0) == 0)  { iall  = TRUE; }
 177    } 
 178    if (ilist || iall || argc == 2 ) {
 179        setext(infile, "shp");
 180        printf("DESCRIBE: %s\n",infile);
 181        strcpy(outfile,"");
 182    }
 183/* -------------------------------------------------------------------- */
 184/*	Look for other functions on the command line. (SELECT, UNIT)  	*/
 185/* -------------------------------------------------------------------- */
 186    for (i = 3; i < argc; i++)
 187    {
 188    	if ((strncasecmp2(argv[i],  "SEL",3) == 0) ||
 189            (strncasecmp2(argv[i],  "UNSEL",5) == 0))
 190    	{
 191            if (strncasecmp2(argv[i],  "UNSEL",5) == 0) iunselect=TRUE;
 192    	    i++;
 193    	    if (i >= argc) error();
 194    	    strcpy(selectitem,argv[i]);
 195    	    i++;
 196    	    if (i >= argc) error();
 197    	    selcount=0;
 198    	    strcpy(temp,argv[i]);
 199    	    cpt=temp;
 200    	    tj = atoi(cpt);
 201    	    ti = 0;
 202    	    while (tj>0) {
 203                selectvalues[selcount] = tj;
 204                while( *cpt >= '0' && *cpt <= '9')
 205                    cpt++; 
 206                while( *cpt > '\0' && (*cpt < '0' || *cpt > '9') )
 207                    cpt++; 
 208                tj=atoi(cpt);
 209                selcount++;
 210    	    }
 211    	    iselect=TRUE;
 212    	}  /*** End SEL & UNSEL ***/
 213    	else
 214            if ((strncasecmp2(argv[i], "CLIP",4) == 0) ||
 215                (strncasecmp2(argv[i],  "ERASE",5) == 0))
 216            {
 217                if (strncasecmp2(argv[i],  "ERASE",5) == 0) ierase=TRUE;
 218                i++;
 219                if (i >= argc) error();
 220                strcpy(clipfile,argv[i]);
 221                sscanf(argv[i],"%lf",&cxmin);
 222                i++;
 223                if (i >= argc) error();
 224                if (strncasecmp2(argv[i],  "BOUND",5) == 0) {
 225                    setext(clipfile, "shp");
 226                    hSHP = SHPOpen( clipfile, "rb" );
 227                    if( hSHP == NULL )
 228                    {
 229                        printf( "ERROR: Unable to open the clip shape file:%s\n", clipfile );
 230                        exit( 1 );
 231                    }
 232                    SHPGetInfo( hSHPappend, NULL, NULL,
 233                                adfBoundsMin, adfBoundsMax );
 234                    cxmin = adfBoundsMin[0];
 235                    cymin = adfBoundsMin[1];
 236                    cxmax = adfBoundsMax[0];
 237                    cymax = adfBoundsMax[1];
 238                    printf("Theme Clip Boundary: (%lf,%lf) - (%lf,%lf)\n",
 239                           cxmin, cymin, cxmax, cymax);
 240                    ibound=TRUE;
 241                } else {  /*** xmin,ymin,xmax,ymax ***/
 242                    sscanf(argv[i],"%lf",&cymin);
 243                    i++;
 244                    if (i >= argc) error();
 245                    sscanf(argv[i],"%lf",&cxmax);
 246                    i++;
 247                    if (i >= argc) error();
 248                    sscanf(argv[i],"%lf",&cymax);
 249                    printf("Clip Box: (%lf,%lf) - (%lf,%lf)\n",cxmin, cymin, cxmax, cymax);
 250                }
 251                i++;
 252                if (i >= argc) error();
 253                if      (strncasecmp2(argv[i], "CUT",3) == 0)    icut=TRUE;
 254                else if (strncasecmp2(argv[i], "TOUCH",5) == 0)  itouch=TRUE;
 255                else if (strncasecmp2(argv[i], "INSIDE",6) == 0) iinside=TRUE;
 256                else error();
 257                iclip=TRUE;
 258            } /*** End CLIP & ERASE ***/
 259            else if (strncasecmp2(argv[i],  "FACTOR",0) == 0)
 260                {
 261                i++;
 262    	        if (i >= argc) error();
 263    	        infactor=findunit(argv[i]);
 264    	        if (infactor == 0) error();
 265                iunit=TRUE;
 266                i++;
 267    	        if (i >= argc) error();
 268    	        outfactor=findunit(argv[i]);
 269    	        if (outfactor == 0)
 270    	        {
 271                   sscanf(argv[i],"%lf",&factor);
 272                   if (factor == 0) error();
 273                }
 274                if (factor == 0)
 275                {
 276                  if (infactor ==0)
 277                  { puts("ERROR: Input unit must be defined before output unit"); exit(); }
 278                  factor=infactor/outfactor;
 279                }
 280                printf("Output file coordinate values will be factored by %lg\n",factor);
 281                ifactor=(factor != 1); /* True if a valid factor */
 282             } /*** End FACTOR ***/
 283             else if (strncasecmp2(argv[i],"SHIFT",5) == 0)
 284                {
 285                i++;
 286                if (i >= argc) error();
 287                sscanf(argv[i],"%lf",&xshift);
 288                i++;
 289                if (i >= argc) error();
 290                sscanf(argv[i],"%lf",&yshift);
 291                iunit=TRUE;
 292                printf("X Shift: %lg   Y Shift: %lg\n",xshift,yshift);
 293             } /*** End SHIFT ***/
 294             else {
 295                printf("ERROR: Unknown function %s\n",argv[i]);  error();
 296                }
 297    }
 298/* -------------------------------------------------------------------- */
 299/*	If there is no data in this file let the user know.		*/
 300/* -------------------------------------------------------------------- */
 301    openfiles();  /* Open the infile and the outfile for shape and dbf. */
 302    if( DBFGetFieldCount(hDBF) == 0 )
 303    {
 304	puts( "There are no fields in this table!" );
 305	exit( 1 );
 306    }
 307/* -------------------------------------------------------------------- */
 308/*      Print out the file bounds.                                      */
 309/* -------------------------------------------------------------------- */
 310    iRecord = DBFGetRecordCount( hDBF );
 311    SHPGetInfo( hSHP, NULL, NULL, adfBoundsMin, adfBoundsMax );
 312
 313    printf( "Input Bounds:  (%lg,%lg) - (%lg,%lg)   Entities: %d   DBF: %d\n",
 314	    adfBoundsMin[0], adfBoundsMin[1],
 315            adfBoundsMax[0], adfBoundsMax[1],
 316            nEntities, iRecord );
 317	    
 318    if (strcmp(outfile,"") == 0) /* Describe the shapefile; No other functions */
 319    {
 320    	ti = DBFGetFieldCount( hDBF );
 321	showitems();
 322	exit(0);
 323    }
 324     
 325    if (iclip) check_theme_bnd();
 326    
 327    jRecord = DBFGetRecordCount( hDBFappend );
 328    SHPGetInfo( hSHPappend, NULL, NULL, adfBoundsMin, adfBoundsMax );
 329    if (nEntitiesAppend == 0)
 330        puts("New Output File\n");
 331    else
 332        printf( "Append Bounds: (%lg,%lg)-(%lg,%lg)   Entities: %d  DBF: %d\n",
 333                adfBoundsMin[0], adfBoundsMin[1],
 334                adfBoundsMax[0], adfBoundsMax[1],
 335                nEntitiesAppend, jRecord );
 336    
 337/* -------------------------------------------------------------------- */
 338/*	Find matching fields in the append file or add new items.       */
 339/* -------------------------------------------------------------------- */
 340    mergefields();
 341/* -------------------------------------------------------------------- */
 342/*	Find selection field if needed.                                 */
 343/* -------------------------------------------------------------------- */
 344    if (iselect)    findselect();
 345
 346/* -------------------------------------------------------------------- */
 347/*  Read all the records 						*/
 348/* -------------------------------------------------------------------- */
 349    jRecord = DBFGetRecordCount( hDBFappend );
 350    for( iRecord = 0; iRecord < nEntities; iRecord++)  /** DBFGetRecordCount(hDBF) **/
 351    {
 352/* -------------------------------------------------------------------- */
 353/*      SELECT for values if needed. (Can the record be skipped.)       */
 354/* -------------------------------------------------------------------- */
 355        if (iselect)
 356            if (selectrec() == 0) goto SKIP_RECORD;   /** SKIP RECORD **/
 357
 358/* -------------------------------------------------------------------- */
 359/*      Read a Shape record                                             */
 360/* -------------------------------------------------------------------- */
 361        psCShape = SHPReadObject( hSHP, iRecord );
 362
 363/* -------------------------------------------------------------------- */
 364/*      Clip coordinates of shapes if needed.                           */
 365/* -------------------------------------------------------------------- */
 366        if (iclip)
 367            if (clip_boundary() == 0) goto SKIP_RECORD; /** SKIP RECORD **/
 368
 369/* -------------------------------------------------------------------- */
 370/*      Read a DBF record and copy each field.                          */
 371/* -------------------------------------------------------------------- */
 372	for( i = 0; i < DBFGetFieldCount(hDBF); i++ )
 373	{
 374/* -------------------------------------------------------------------- */
 375/*      Store the record according to the type and formatting           */
 376/*      information implicit in the DBF field description.              */
 377/* -------------------------------------------------------------------- */
 378            if (pt[i] > -1)  /* if the current field exists in output file */
 379            {
 380                switch( DBFGetFieldInfo( hDBF, i, NULL, &iWidth, &iDecimals ) )
 381                {
 382                  case FTString:
 383                    DBFWriteStringAttribute(hDBFappend, jRecord, pt[i],
 384                                            (DBFReadStringAttribute( hDBF, iRecord, i )) );
 385                    break;
 386
 387                  case FTInteger:
 388                    DBFWriteIntegerAttribute(hDBFappend, jRecord, pt[i],
 389                                             (DBFReadIntegerAttribute( hDBF, iRecord, i )) );
 390                    break;
 391
 392                  case FTDouble:
 393                    DBFWriteDoubleAttribute(hDBFappend, jRecord, pt[i],
 394                                            (DBFReadDoubleAttribute( hDBF, iRecord, i )) );
 395                    break;
 396                }
 397            }
 398	}
 399	jRecord++;
 400/* -------------------------------------------------------------------- */
 401/*      Change FACTOR and SHIFT coordinates of shapes if needed.        */
 402/* -------------------------------------------------------------------- */
 403         if (iunit)
 404        {
 405	    for( j = 0; j < psCShape->nVertices; j++ ) 
 406	    {
 407                psCShape->padfX[j] = psCShape->padfX[j] * factor + xshift;
 408                psCShape->padfY[j] = psCShape->padfY[j] * factor + yshift;
 409	    }
 410        }
 411        
 412/* -------------------------------------------------------------------- */
 413/*      Write the Shape record after recomputing current extents.       */
 414/* -------------------------------------------------------------------- */
 415        SHPComputeExtents( psCShape );
 416        SHPWriteObject( hSHPappend, -1, psCShape );
 417
 418      SKIP_RECORD:
 419        SHPDestroyObject( psCShape );
 420        psCShape = NULL;
 421        j=0;
 422    }
 423
 424/* -------------------------------------------------------------------- */
 425/*      Print out the # of Entities and the file bounds.                */
 426/* -------------------------------------------------------------------- */
 427    jRecord = DBFGetRecordCount( hDBFappend );
 428    SHPGetInfo( hSHPappend, &nEntitiesAppend, &nShapeTypeAppend,
 429                adfBoundsMin, adfBoundsMax );
 430    
 431    printf( "Output Bounds: (%lg,%lg) - (%lg,%lg)   Entities: %d  DBF: %d\n\n",
 432	    adfBoundsMin[0], adfBoundsMin[1],
 433            adfBoundsMax[0], adfBoundsMax[1],
 434            nEntitiesAppend, jRecord );
 435
 436/* -------------------------------------------------------------------- */
 437/*      Close the both shapefiles.                                      */
 438/* -------------------------------------------------------------------- */
 439    SHPClose( hSHP );
 440    SHPClose( hSHPappend );
 441    DBFClose( hDBF );
 442    DBFClose( hDBFappend );
 443    if (nEntitiesAppend == 0) {
 444       puts("Remove the output files.");
 445       setext(outfile, "dbf");
 446       remove(outfile);
 447       setext(outfile, "shp");
 448       remove(outfile);
 449       setext(outfile, "shx");
 450       remove(outfile);
 451    }
 452    return( 0 );
 453}
 454
 455
 456/************************************************************************/
 457/*                             openfiles()                              */
 458/************************************************************************/
 459
 460void openfiles() {
 461/* -------------------------------------------------------------------- */
 462/*      Open the DBF file.                                              */
 463/* -------------------------------------------------------------------- */
 464    setext(infile, "dbf");
 465    hDBF = DBFOpen( infile, "rb" );
 466    if( hDBF == NULL )
 467    {
 468	printf( "ERROR: Unable to open the input DBF:%s\n", infile );
 469	exit( 1 );
 470    }
 471/* -------------------------------------------------------------------- */
 472/*      Open the append DBF file.                                       */
 473/* -------------------------------------------------------------------- */
 474    if (strcmp(outfile,"")) {
 475        setext(outfile, "dbf");
 476        hDBFappend = DBFOpen( outfile, "rb+" );
 477        newdbf=0;
 478        if( hDBFappend == NULL )
 479        {
 480            newdbf=1;
 481            hDBFappend = DBFCreate( outfile );
 482            if( hDBFappend == NULL )
 483            {
 484                printf( "ERROR: Unable to open the append DBF:%s\n", outfile );
 485                exit( 1 );
 486            }
 487        }
 488    }
 489/* -------------------------------------------------------------------- */
 490/*      Open the passed shapefile.                                      */
 491/* -------------------------------------------------------------------- */
 492    setext(infile, "shp");
 493    hSHP = SHPOpen( infile, "rb" );
 494
 495    if( hSHP == NULL )
 496    {
 497	printf( "ERROR: Unable to open the input shape file:%s\n", infile );
 498	exit( 1 );
 499    }
 500
 501    SHPGetInfo( hSHP, &nEntities, &nShapeType, NULL, NULL );
 502
 503/* -------------------------------------------------------------------- */
 504/*      Open the passed append shapefile.                               */
 505/* -------------------------------------------------------------------- */
 506    if (strcmp(outfile,"")) {
 507        setext(outfile, "shp");
 508        hSHPappend = SHPOpen( outfile, "rb+" );
 509
 510        if( hSHPappend == NULL )
 511        {
 512            hSHPappend = SHPCreate( outfile, nShapeType );
 513            if( hSHPappend == NULL )
 514            {
 515                printf( "ERROR: Unable to open the append shape file:%s\n",
 516                        outfile );
 517                exit( 1 );
 518            }
 519        }
 520        SHPGetInfo( hSHPappend, &nEntitiesAppend, &nShapeTypeAppend,
 521                    NULL, NULL );
 522
 523        if (nShapeType != nShapeTypeAppend) 
 524        {
 525            puts( "ERROR: Input and Append shape files are of different types.");
 526            exit( 1 );
 527        }
 528    }
 529}
 530
 531/* -------------------------------------------------------------------- */
 532/*	Change the extension.  If there is any extension on the 	*/
 533/*	filename, strip it off and add the new extension                */
 534/* -------------------------------------------------------------------- */
 535void setext(char *pt, char *ext)
 536{
 537int i;
 538    for( i = strlen(pt)-1; 
 539	 i > 0 && pt[i] != '.' && pt[i] != '/' && pt[i] != '\\';
 540	 i-- ) {}
 541
 542    if( pt[i] == '.' )
 543        pt[i] = '\0';
 544        
 545    strcat(pt,".");
 546    strcat(pt,ext);
 547}
 548
 549
 550
 551/* -------------------------------------------------------------------- */
 552/*	Find matching fields in the append file.                        */
 553/*      Output file must have zero records to add any new fields.       */
 554/* -------------------------------------------------------------------- */
 555void mergefields()
 556{
 557    int i,j;
 558    ti = DBFGetFieldCount( hDBF );
 559    tj = DBFGetFieldCount( hDBFappend );
 560    /* Create a pointer array for the max # of fields in the output file */
 561    pt = (int *) malloc( (ti+tj+1) * sizeof(int) ); 
 562    
 563    for( i = 0; i < ti; i++ )
 564    {
 565       pt[i]= -1;  /* Initial pt values to -1 */
 566    }
 567    /* DBF must be empty before adding items */
 568    jRecord = DBFGetRecordCount( hDBFappend );
 569    for( i = 0; i < ti; i++ )
 570    {
 571	iType = DBFGetFieldInfo( hDBF, i, iszTitle, &iWidth, &iDecimals );
 572        found=FALSE;
 573        {
 574      	    for( j = 0; j < tj; j++ )   /* Search all field names for a match */
 575    	    {
 576	        jType = DBFGetFieldInfo( hDBFappend, j, jszTitle, &jWidth, &jDecimals );
 577	        if (iType == jType && (strcmp(iszTitle, jszTitle) == 0) )
 578	        {
 579	            if (found || newdbf)
 580	            {
 581	                if (i == j)  pt[i]=j;
 582	                printf("Warning: Duplicate field name found (%s)\n",iszTitle);
 583	                /* Duplicate field name
 584	                   (Try to guess the correct field by position) */
 585	            }
 586	            else
 587	            {
 588	            	pt[i]=j;  found=TRUE; 
 589	            }
 590	        }
 591	    }
 592	}
 593	
 594	if (pt[i] == -1  && (! found) )  /* Try to force into an existing field */
 595	{                                /* Ignore the field name, width, and decimal places */
 596	    jType = DBFGetFieldInfo( hDBFappend, j, jszTitle, &jWidth, &jDecimals );
 597	    if (iType == jType) 
 598	    {
 599	    	pt[i]=i;  found=1;
 600	    }
 601	}
 602	if ( (! found) &&  jRecord == 0)  /* Add missing field to the append table */
 603	{                 /* The output DBF must be is empty */
 604	    pt[i]=tj;
 605	    tj++;
 606	    if( DBFAddField( hDBFappend, iszTitle, iType, iWidth, iDecimals )
 607                == -1 )
 608	    {
 609		printf( "Warning: DBFAddField(%s, TYPE:%d, WIDTH:%d  DEC:%d, ITEM#:%d of %d) failed.\n",
 610		         iszTitle, iType, iWidth, iDecimals, (i+1), (ti+1) );
 611		pt[i]=-1;
 612	    }
 613	}
 614    }
 615}
 616
 617
 618void findselect()
 619{
 620    /* Find the select field name */
 621    iselectitem = -1;
 622    for( i = 0; i < ti  &&  iselectitem < 0; i++ )
 623    {
 624	iType = DBFGetFieldInfo( hDBF, i, iszTitle, &iWidth, &iDecimals );
 625        if (strncasecmp2(iszTitle, selectitem, 0) == 0) iselectitem = i;
 626    }
 627    if (iselectitem == -1) 
 628    {
 629        printf("Warning: Item not found for selection (%s)\n",selectitem);
 630        iselect = FALSE;
 631        iall = FALSE;
 632	showitems();
 633        printf("Continued... (Selecting entire file)\n");
 634    }
 635    /* Extract all of the select values (by field type) */
 636    
 637}
 638
 639void showitems()
 640{
 641char      stmp[40],slow[40],shigh[40];
 642double    dtmp,dlow,dhigh,dsum,mean;
 643long int  itmp,ilow,ihigh,isum;
 644long int  maxrec;
 645char      *pt;
 646
 647        printf("Available Items: (%d)",ti);
 648        maxrec = DBFGetRecordCount(hDBF);
 649        if (maxrec > 5000 && ! iall) 
 650                { maxrec=5000; printf("  ** ESTIMATED RANGES (MEAN)  For more records use \"All\""); }
 651          else  { printf("          RANGES (MEAN)"); }
 652          
 653        for( i = 0; i < ti; i++ )
 654        {
 655	    switch( DBFGetFieldInfo( hDBF, i, iszTitle, &iWidth, &iDecimals ) )
 656	    {
 657	      case FTString:
 658	        strcpy(slow, "~");
 659	        strcpy(shigh,"\0");
 660                printf("\n  String  %3d  %-16s",iWidth,iszTitle);
 661	        for( iRecord = 0; iRecord < maxrec; iRecord++ ) {
 662		        strncpy(stmp,DBFReadStringAttribute( hDBF, iRecord, i ),39);
 663		        if (strcmp(stmp,"!!") > 0) {
 664		          if (strncasecmp2(stmp,slow,0)  < 0) strncpy(slow, stmp,39);
 665		          if (strncasecmp2(stmp,shigh,0) > 0) strncpy(shigh,stmp,39);
 666		        }
 667		}
 668		pt=slow+strlen(slow)-1; 
 669		while(*pt == ' ') { *pt='\0'; pt--; }
 670		pt=shigh+strlen(shigh)-1;
 671		while(*pt == ' ') { *pt='\0'; pt--; }
 672		if (strncasecmp2(slow,shigh,0) < 0)		printf("%s to %s",slow,shigh);
 673		else if (strncasecmp2(slow,shigh,0) == 0)	printf("= %s",slow);
 674						    else	printf("No Values");
 675		break;
 676	      case FTInteger:
 677		printf("\n  Integer %3d  %-16s",iWidth,iszTitle);
 678		ilow =  1999999999;
 679		ihigh= -1999999999;
 680		isum =  0;
 681	        for( iRecord = 0; iRecord < maxrec; iRecord++ ) {
 682		        itmp = DBFReadIntegerAttribute( hDBF, iRecord, i );
 683		        if (ilow > itmp)  ilow = itmp;
 684		        if (ihigh < itmp) ihigh = itmp;
 685		        isum = isum + itmp;
 686		}
 687		mean=isum/maxrec;
 688		if (ilow < ihigh)       printf("%d to %d \t(%.1f)",ilow,ihigh,mean);
 689		else if (ilow == ihigh) printf("= %d",ilow);
 690		                   else printf("No Values");
 691		break;
 692
 693	      case FTDouble:
 694		printf("\n  Real  %3d,%d  %-16s",iWidth,iDecimals,iszTitle);
 695		dlow =  999999999999999.0;
 696		dhigh= -999999999999999.0;
 697		dsum =  0;
 698	        for( iRecord = 0; iRecord < maxrec; iRecord++ ) {
 699		        dtmp = DBFReadDoubleAttribute( hDBF, iRecord, i );
 700		        if (dlow > dtmp) dlow = dtmp;
 701		        if (dhigh < dtmp) dhigh = dtmp;
 702		        dsum = dsum + dtmp;
 703		}
 704		mean=dsum/maxrec;
 705		sprintf(stmp,"%%.%df to %%.%df \t(%%.%df)",iDecimals,iDecimals,iDecimals);
 706		if (dlow < dhigh)       printf(stmp,dlow,dhigh,mean);
 707		else if (dlow == dhigh) {
 708				        sprintf(stmp,"= %%.%df",iDecimals);
 709		                        printf(stmp,dlow);
 710		                        }
 711		else printf("No Values");
 712		break;
 713
 714	    }
 715
 716        }
 717	printf("\n");
 718}
 719
 720int selectrec()
 721{
 722long int value, ty;
 723
 724   ty = DBFGetFieldInfo( hDBF, iselectitem, NULL, &iWidth, &iDecimals);
 725      switch(ty)
 726      {
 727      case FTString:
 728        puts("Invalid Item");
 729        iselect=FALSE;
 730	break;
 731      case FTInteger:
 732        value = DBFReadIntegerAttribute( hDBF, iRecord, iselectitem );
 733        for (j = 0; j<selcount; j++)
 734          {
 735          if (selectvalues[j] == value)
 736               if (iunselect) return(0);  /* Keep this record */
 737                        else  return(1);  /* Skip this record */
 738          }
 739	break;
 740      case FTDouble:
 741        puts("Invalid Item");
 742        iselect=FALSE;
 743        break;
 744      }
 745      if (iunselect) return(1);  /* Skip this record */
 746               else  return(0);  /* Keep this record */
 747}
 748
 749
 750int check_theme_bnd()
 751{
 752    if ( (adfBoundsMin[0] >= cxmin) && (adfBoundsMax[0] <= cxmax) &&
 753         (adfBoundsMin[1] >= cymin) && (adfBoundsMax[1] <= cymax) )
 754    {   /** Theme is totally inside clip area **/
 755        if (ierase) nEntities=0; /** SKIP THEME  **/
 756        else   iclip=FALSE; /** WRITE THEME (Clip not needed) **/
 757    }
 758            
 759    if ( ( (adfBoundsMin[0] < cxmin) && (adfBoundsMax[0] < cxmin) ) ||
 760         ( (adfBoundsMin[1] < cymin) && (adfBoundsMax[1] < cymin) ) ||
 761         ( (adfBoundsMin[0] > cxmax) && (adfBoundsMax[0] > cxmax) ) ||
 762         ( (adfBoundsMin[1] > cymax) && (adfBoundsMax[1] > cymax) ) )
 763    {   /** Theme is totally outside clip area **/
 764        if (ierase) iclip=FALSE; /** WRITE THEME (Clip not needed) **/
 765             else   nEntities=0; /** SKIP THEME  **/
 766    }
 767            
 768    if (nEntities == 0)
 769        puts("WARNING: Theme is outside the clip area."); /** SKIP THEME  **/
 770}
 771
 772clip_boundary()
 773{
 774    int  inside;
 775    int  prev_outside;
 776    int  i2;
 777    int  j2;
 778    
 779       /*** FIRST check the boundary of the feature ***/
 780       if ( ( (psCShape->dfXMin < cxmin) && (psCShape->dfXMax < cxmin) ) ||
 781            ( (psCShape->dfYMin < cymin) && (psCShape->dfYMax < cymin) ) ||
 782            ( (psCShape->dfXMin > cxmax) && (psCShape->dfXMax > cxmax) ) ||
 783            ( (psCShape->dfYMin > cymax) && (psCShape->dfYMax > cymax) ) )
 784            {   /** Feature is totally outside clip area **/
 785            	if (ierase) return(1); /** WRITE RECORD **/
 786            	     else   return(0); /** SKIP  RECORD **/
 787            }
 788       
 789       if ( (psCShape->dfXMin >= cxmin) && (psCShape->dfXMax <= cxmax) &&
 790            (psCShape->dfYMin >= cymin) && (psCShape->dfYMax <= cymax) )
 791            {   /** Feature is totally inside clip area **/
 792            	if (ierase) return(0); /** SKIP  RECORD **/
 793            	     else   return(1); /** WRITE RECORD **/
 794            }
 795            
 796       if (iinside) 
 797            { /** INSIDE * Feature might touch the boundary or could be outside **/
 798            if (ierase)  return(1); /** WRITE RECORD **/
 799                 else    return(0); /** SKIP  RECORD **/
 800            }
 801       
 802       if (itouch)
 803          {   /** TOUCH **/
 804          if ( ( (psCShape->dfXMin <= cxmin) || (psCShape->dfXMax >= cxmax) ) && 
 805                 (psCShape->dfYMin >= cymin) && (psCShape->dfYMax <= cymax)    )
 806               {   /** Feature intersects the clip boundary only on the X axis **/
 807               if (ierase) return(0); /** SKIP  RECORD **/
 808                    else   return(1); /** WRITE RECORD **/
 809               }
 810
 811          if (   (psCShape->dfXMin >= cxmin) && (psCShape->dfXMax <= cxmax)   && 
 812               ( (psCShape->dfYMin <= cymin) || (psCShape->dfYMax >= cymax) )  )
 813               {   /** Feature intersects the clip boundary only on the Y axis **/
 814               if (ierase) return(0); /** SKIP  RECORD **/
 815                    else   return(1); /** WRITE RECORD **/
 816               }
 817               
 818          for( j2 = 0; j2 < psCShape->nVertices; j2++ ) 
 819               {   /** At least one vertex must be inside the clip boundary **/
 820               if ( (psCShape->padfX[j2] >= cxmin  &&  psCShape->padfX[j2] <= cxmax) ||
 821                    (psCShape->padfY[j2] >= cymin  &&  psCShape->padfY[j2] <= cymax)  )
 822                    if (ierase) return(0); /** SKIP  RECORD **/
 823                         else   return(1); /** WRITE RECORD **/
 824               }
 825               
 826          /** All vertices are outside the clip boundary **/ 
 827          if (ierase) return(1); /** WRITE RECORD **/
 828               else   return(0); /** SKIP  RECORD **/
 829          }   /** End TOUCH **/
 830          
 831       if (icut)
 832          {   /** CUT **/
 833          /*** Check each vertex in the feature with the Boundary and "CUT" ***/
 834          /*** THIS CODE WAS NOT COMPLETED!  READ NOTE AT THE BOTTOM ***/
 835          i2=0;
 836          prev_outside=FALSE;
 837          for( j2 = 0; j2 < psCShape->nVertices; j2++ ) 
 838             {
 839             inside = psCShape->padfX[j2] >= cxmin  &&  psCShape->padfX[j2] <= cxmax  &&
 840                      psCShape->padfY[j2] >= cymin  &&  psCShape->padfY[j2] <= cymax ;
 841                      
 842             if (ierase) inside=(! inside);
 843             if (inside)
 844                 {
 845                 if (i2 != j2)
 846                     {
 847                     if (prev_outside)
 848                         {
 849                         /*** AddIntersection(i2);   /*** Add intersection ***/
 850                         prev_outside=FALSE;
 851                         }
 852                     psCShape->padfX[i2]=psCShape->padfX[j2];     /** move vertex **/
 853                     psCShape->padfY[i2]=psCShape->padfY[j2];
 854                     }
 855                 i2++;
 856                 } else {
 857                 if ( (! prev_outside) && (j2 > 0) )
 858                     {
 859                     /*** AddIntersection(i2);   /*** Add intersection (Watch out for j2==i2-1) ***/
 860                     /*** Also a polygon may overlap twice and will split into a several parts  ***/
 861                     prev_outside=TRUE;
 862                     }
 863                 }
 864             }
 865             
 866        printf("Vertices:%d   OUT:%d   Number of Parts:%d\n",
 867                psCShape->nVertices,i2, psCShape->nParts );
 868               
 869             psCShape->nVertices = i2;
 870             
 871             if (i2 < 2) return(0); /** SKIP RECORD **/
 872             /*** (WE ARE NOT CREATING INTERESECTIONS and some lines could be reduced to one point) **/
 873        
 874             if (i2 == 0) return(0); /** SKIP  RECORD **/
 875                  else    return(1); /** WRITE RECORD **/
 876          }  /** End CUT **/
 877}
 878
 879
 880/************************************************************************/
 881/*                            strncasecmp2()                            */
 882/*                                                                      */
 883/*      Compare two strings up to n characters                          */
 884/*      If n=0 then s1 and s2 must be an exact match                    */
 885/************************************************************************/
 886
 887int strncasecmp2(char *s1, char *s2, int n)
 888
 889{
 890int j,i;
 891   if (n<1) n=strlen(s1)+1;
 892   for (i=0; i<n; i++)
 893   {
 894      if (*s1 != *s2)
 895      {
 896         if (*s1 >= 'a' && *s1 <= 'z') {
 897            j=*s1-32;
 898            if (j != *s2) return(*s1-*s2);
 899         } else {
 900            if (*s1 >= 'A' && *s1 <= 'Z') { j=*s1+32; }
 901                                   else   { j=*s1;    }
 902            if (j != *s2) return(*s1-*s2); 
 903         }
 904      }
 905      s1++;
 906      s2++;
 907   }
 908   return(0);
 909}
 910
 911
 912#define  NKEYS (sizeof(unitkeytab) / sizeof(struct unitkey))
 913findunit(unit)
 914   char *unit;
 915   {
 916   struct unitkey {
 917     char   *name;
 918     double value;
 919   } unitkeytab[] = {
 920     "CM",            39.37,
 921     "CENTIMETER",    39.37,
 922     "CENTIMETERS",   39.37,  /** # of inches * 100 in unit **/
 923     "METER",          3937,
 924     "METERS",         3937,
 925     "KM",          3937000,
 926     "KILOMETER",   3937000, 
 927     "KILOMETERS",  3937000,
 928     "INCH",            100,
 929     "INCHES",          100,
 930     "FEET",           1200,
 931     "FOOT",           1200,
 932     "YARD",           3600,
 933     "YARDS",          3600,       
 934     "MILE",        6336000,
 935     "MILES",       6336000  
 936   };
 937
 938   double unitfactor=0;
 939   for (j = 0; j < NKEYS; j++) {
 940    if (strncasecmp2(unit, unitkeytab[j].name, 0) == 0) unitfactor=unitkeytab[j].value;
 941   }
 942   return(unitfactor);
 943}
 944
 945/* -------------------------------------------------------------------- */
 946/*      Display a usage message.                                        */
 947/* -------------------------------------------------------------------- */
 948void error()
 949    {	
 950	puts( "The program will append to an existing shape file or it will" );
 951	puts( "create a new file if needed." );
 952	puts( "Only the items in the first output file will be preserved." );
 953	puts( "When an item does not match with the append theme then the item");
 954	puts( "might be placed to an existing item at the same position and type." );
 955	puts( "  OTHER FUNCTIONS:" );
 956	puts( "  - Describe all items in the dbase file (Use ALL for more than 5000 recs.)");
 957	puts( "  - Select a group of shapes from a comma separated selection list.");
 958	puts( "  - UnSelect a group of shapes from a comma separated selection list.");
 959	puts( "  - Clip boundary extent or by theme boundary." );
 960	puts( "      Touch writes all the shapes that touch the boundary.");
 961	puts( "      Inside writes all the shapes that are completely within the boundary.");
 962	puts( "      Boundary clips are only the min and max of a theme boundary." );
 963	puts( "  - Erase boundary extent or by theme boundary." );
 964	puts( "      Erase is the direct opposite of the Clip function." );
 965	puts( "  - Change coordinate value units between meters and feet.");
 966	puts( "      There is no way to determine the input unit of a shape file.");
 967	puts( "      Skip this function if the shape file is already in the correct unit.");
 968	puts( "      Clip and Erase will be done before the unit is changed.");
 969	puts( "      A shift will be done after the unit is changed."); 
 970	puts( "  - Shift X and Y coordinates.\n" );
 971	puts( "Finally, There can only be one select or unselect in the command line.");
 972	puts( "         There can only be one clip or erase in the command line.");
 973	puts( "         There can only be one unit and only one shift in the command line.\n");
 974	puts( "Ex: shputils in.shp out.shp   SELECT countycode 3,5,9,13,17,27");
 975	puts( "    shputils in.shp out.shp   CLIP   10 10 90 90 Touch   FACTOR Meter Feet");
 976	puts( "    shputils in.shp out.shp   FACTOR Meter 3.0");
 977	puts( "    shputils in.shp out.shp   CLIP   clip.shp Boundary Touch   SHIFT 40 40");
 978	puts( "    shputils in.shp out.shp   SELECT co 112   CLIP clip.shp Boundary Touch\n");
 979	puts( "USAGE: shputils  <DescribeShape>   {ALL}");
 980	puts( "USAGE: shputils  <InputShape>  <OutShape|AppendShape>" );
 981	puts( "   { <FACTOR>       <FEET|MILES|METERS|KM> <FEET|MILES|METERS|KM|factor> }" );
 982	puts( "   { <SHIFT>        <xshift> <yshift> }" );
 983	puts( "   { <SELECT|UNSEL> <Item> <valuelist> }" );
 984	puts( "   { <CLIP|ERASE>   <xmin> <ymin> <xmax> <ymax> <TOUCH|INSIDE|CUT> }" );
 985	puts( "   { <CLIP|ERASE>   <theme>      <BOUNDARY>     <TOUCH|INSIDE|CUT> }" );
 986	puts( "     Note: CUT is not complete and does not create intersections.");
 987	puts( "           For more information read programmer comment.");
 988	
 989	/****   Clip functions for Polygon and Cut is not supported
 990	There are several web pages that describe methods of doing this function.
 991	It seem easy to impliment until you start writting code.  I don't have the
 992	time to add these functions but a did leave a simple cut routine in the 
 993	program that can be called by using CUT instead of TOUCH in the 
 994	CLIP or ERASE functions.  It does not add the intersection of the line and
 995	the clip box, so polygons could look incomplete and lines will come up short.
 996	
 997	Information about clipping lines with a box:
 998           http://www.csclub.uwaterloo.ca/u/mpslager/articles/sutherland/wr.html
 999        Information about finding the intersection of two lines:
1000	   http://www.whisqu.se/per/docs/math28.htm
1001	   
1002THE CODE LOOKS LIKE THIS:
1003 ********************************************************	  
1004void Intersect_Lines(float x0,float y0,float x1,float y1,
1005                     float x2,float y2,float x3,float y3,
1006                     float *xi,float *yi)
1007                     {
1008//  this function computes the intersection of the sent lines
1009//  and returns the intersection point, note that the function assumes
1010//  the lines intersect. the function can handle vertical as well
1011//  as horizontal lines. note the function isn't very clever, it simply
1012//  applies the math, but we don't need speed since this is a
1013//  pre-processing step
1014//  The Intersect_lines program came from (http://www.whisqu.se/per/docs/math28.htm)
1015
1016float a1,b1,c1, // constants of linear equations 
1017      a2,b2,c2,
1018      det_inv,  // the inverse of the determinant of the coefficientmatrix
1019      m1,m2;    // the slopes of each line
1020      
1021// compute slopes, note the cludge for infinity, however, this will
1022// be close enough
1023if ((x1-x0)!=0)
1024   m1 = (y1-y0)/(x1-x0);
1025else
1026   m1 = (float)1e+10;  // close enough to infinity
1027   
1028   
1029if ((x3-x2)!=0) 
1030   m2 = (y3-y2)/(x3-x2);
1031else
1032   m2 = (float)1e+10;  // close enough to infinity
1033   
1034// compute constants
1035a1 = m1;
1036a2 = m2;
1037b1 = -1;
1038b2 = -1;
1039c1 = (y0-m1*x0);
1040c2 = (y2-m2*x2);
1041// compute the inverse of the determinate
1042det_inv = 1/(a1*b2 - a2*b1);
1043// use Kramers rule to compute xi and yi
1044*xi=((b1*c2 - b2*c1)*det_inv);
1045*yi=((a2*c1 - a1*c2)*det_inv);
1046} // end Intersect_Lines
1047 **********************************************************/
1048
1049	exit( 1 );
1050    }