Changes between Initial Version and Version 1 of PerlMapScriptExamples35ex8


Ignore:
Timestamp:
Jan 29, 2009, 6:49:07 AM (15 years ago)
Author:
jmckenna
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • PerlMapScriptExamples35ex8

    v1 v1  
     1= tcounts.pl =
     2The url for the data is http://www.highwayengineer.co.medina.oh.us/StickeDB.pdb .
     3{{{
     4#!perl
     5#!/usr/bin/perl
     6#
     7# Copyright (C) 2002, Lowell Filak.
     8# You may distribute this file under the terms of the Artistic
     9# License.
     10#
     11# Given the name of an existing palm database and the name
     12#   of an existing point shapefile this routine will append the points
     13#   in the pdb to the shapefile.
     14# Given just the name of an existing shapefile the routine will attempt
     15#   to use pilot-xfer to download the pdb file and append the points to the
     16#   shapefile.
     17# Given just the name of an existing pdb file this routine will create a
     18#   new shapefile of the points in the pdb file.
     19# Given neither name this routine will attempt to use pilot-xfer to download
     20#   the pdb file and create a new point shapefile from it.
     21# Notes: The fields in the pdb file should match the fields in the
     22#        existing shapefile (dbf) or the assignments will either be
     23#        wrong or will cause the routine to bomb.
     24#   The pdb file for download is assumed to be StickeDB.pdb and the routine
     25#     is written to read sticke style pdb files only.
     26#   The pdb reading section of this routine is not complete but is setup
     27#     somewhat generic and should be extendable to any sticke database
     28#     schema.
     29#   The pilot-xfer download line assumes the default pilot device
     30#     (/dev/pilot) exists.
     31#   The routine also assumes a *n*x system, please change command
     32#     lines accordingly.
     33#   If nad support is needed in proj.4 please verify that the ntv1_can.dat
     34#     file is included before compiling. If not, grab a newer release.
     35#
     36# Required modules are mapscript (installed as part of make install
     37#   http://mapserver.gis.umn.edu),
     38#   Getopt (normally included with Perl),
     39#   Palm (p5-Palm-1.1.5 http://theoryx5.uwinnipeg.ca/CPAN/data/p5-Palm/Palm/Raw.html),
     40#   & XBase (cpan).
     41#   Please download StickeDB.pdb also.
     42#
     43# Additional requirements are: a working pilot-xfer (pilot-link http://www.pilot-link.org/)
     44#   installation,
     45#   a working StickePad.prc and StickePlates.prc (StickeV2Programs
     46#   http://www.cs.ukc.ac.uk/projects/mobicomp/Fieldwork/) on a
     47#   PalmOS handheld device, & a working proj.4 install compiled with
     48#   the optional nad files in place and with the cs2cs
     49#   command working (www.remotesensing.org/proj/).
     50#
     51# Current GPS information: Palm IIIX-PDA, Garmin Etrex Summit-GPS, Blue
     52#   Hills Innovations-Garmin2Palm cable (http://www.blue-hills-innovations.com).
     53#
     54# Suggested run line = ./tcounts.pl -pdbfile=StickeDB -sfile=traffic
     55#
     56# Syntax: tcounts.pl -pdbfile=[in_pdb_filename] -sfile=[out_shapefile_name]
     57#
     58# Include the pdb and pdb-raw modules.
     59use Palm::PDB;
     60use Palm::Raw;
     61#
     62# Include the mapscript module.
     63use mapscript;
     64#
     65# Include the xbase module for creating the dbf records.
     66use XBase;
     67#
     68# Include the getopt module to read input.
     69use Getopt::Long;
     70#
     71# Helpful definitions for StickeDB.pdb:
     72#   I view the structure as very similar to an rdbms.
     73#   Database - refers to the pdb file itself.
     74#   Table - refers to the rdbms-like table name included on every record.
     75#     Note: Each record can belong to a different table or even a different
     76#           table deffinition under the same table name!
     77#     Note: Tables are refered to as 'templates' inside sticke.
     78#   Record - refers to the entire 'data' portion returned by the pdb->data obj.
     79#     Note: Records are refered to as 'notes' inside of sticke.
     80#   Field - refers to the section of the 'data' portion of the record which
     81#           spans from the beginning of one field name to the beginning of the
     82#           next.
     83#     Note: Fields are refered to as both 'fields' and 'items' inside of sticke.
     84#   Part - refers to the sections of a field that define the schema of the
     85#          field (schema - data type, constraints, etc).
     86#
     87# Grab the file names from the input.
     88&GetOptions('pdbfile=s' => \$pdbfile, 'sfile=s' => \$sfile);
     89if ( !$sfile ) {
     90  #
     91  # Create a unique name for the shapefile based on date and process number.
     92
     93  #
     94  # Grab the time.
     95  ($sec,$min,$hr,$mdy,$mnth,$yr,$wdy,$ydy,$isdst) = localtime;
     96  #
     97  # Grab the process id.
     98  $spid = $$;
     99
     100  #
     101  # Create the name & make sure it is no longer than 8 characters.
     102  $sfile = substr("$hr$min$sec$spid", -8);
     103}
     104if( !$pdbfile ) {
     105  #
     106  # Download the pdb file.
     107  system("pilot-xfer -f StickeDB");
     108  #
     109  # Set the pdb file name.
     110  $pdbfile = 'StickeDB';
     111}
     112#
     113# Create the pdb object on the pdb file.
     114my $pdb = new Palm::PDB;
     115$pdb->Load("$pdbfile.pdb");
     116#
     117# How many pdb records are there.
     118my @records = @{$pdb->{records}};
     119my $numrecs = scalar(@records);
     120#
     121# Create the information array for each field/data type.
     122#   Note: The routine is incomplete at this time because we currently only use
     123#    7-number & 4-location but all types are here for documentation.
     124my @types = ('bearing', 'boolean', 'date', 'textline', 'location', 'unused', 'notepad', 'number', 'picklist', 'subnote', 'time');
     125#
     126# Create the field type number-of-parts array.
     127my @parts = (0,0,0,0,23,0,0,11,0,0,0);
     128#
     129# Create the field types unpack string array.
     130#   For location [offset+10]=dontno5, [offset+11]=latdegrees,
     131#    [offset+12]=dontno6, [offset+13]=latminuteswhole,
     132#    [offset+14]=dontno7, [offset+15]=latminutesdecimal,
     133#    [offset+16]=elevation, [offset+17]=dontno8, [offset+18]=longdegrees,
     134#    [offset+19]=dontno9, [offset+20]=longminuteswhole,
     135#    [offset+21]=dontno10, [offset+22]=longminutesdecimal
     136#    Note: At this point I can't find the NSEW/+- indication for lat/lon!
     137#  For number [offset+10]=number
     138my @ustring = (' a*', ' a*', ' a*', ' a*', ' l n a2 n a2 n s a2 n a2 n a2 n', ' a*', ' a*', ' N', ' a*', ' a*', ' a*');
     139#
     140# Create the array for the pdb-field-type to dbf-field-type conversion.
     141my @dbfftype = ('C', 'L', 'D', 'C', 'C', 'C', 'C', 'N', 'C', 'C', 'N');
     142#
     143
     144# Create the array for the pdb-field-size to dbf-field-size conversion.
     145my @dbffsize = ('255', '1', '8', '255', '31', '0', '255', '11', '255', '255', '10');
     146#
     147# Create the array for the pdb-field-decimal to dbf-field-decimal conversion.
     148# No need for this now. All decimals should be null going into the dbf file.
     149#
     150# Create the array for tracking field offsets.
     151#   To be used later while creating dbf records for each gps point.
     152my @offsets = ();
     153#
     154# Create the initial unpack string.
     155my $unpackstr = "";
     156#
     157# Create the initial data array.
     158my @recordinfo = ();
     159#
     160# Initialize the dbf record count to 0.
     161my $dbfreccnt = 0;
     162#
     163# Does the dbf file already exist or does it yet to exist.
     164if ( -e "$sfile.dbf" ) {
     165  #
     166  # Open the existing dbf file for appending to.
     167  $dbh = new XBase "$sfile.dbf" or die XBase->errstr;
     168  #
     169  # To be able to increment the record # starting at the last existing
     170  #   record how many records are there.
     171  $dbfreccnt = $dbh->last_record + 1;
     172}
     173 else {
     174  $dbfreccnt = -1;
     175 }
     176
     177#
     178# Does the shapefile already exist or is it yet to exist.
     179if ( -e "$sfile.shp" ) {
     180  #
     181  # Move the existing shapefile to a temp name.
     182  #   This is done because the -1 option on shapefileObj open
     183  #     only allows for read not append.
     184  #   However as (hopefully) shown below this is not hard to
     185  #     implement inside mapscript.
     186  system("mv $sfile.shp thistemp.shp; mv $sfile.shx thistemp.shx; touch thistemp.dbf"); 
     187  #
     188  # Open the existing file.
     189  $ecounts = new shapefileObj("thistemp",-1);
     190  #
     191  # Create the replacement shapefile.
     192  $tcounts = new shapefileObj("$sfile",$mapscript::MS_SHAPEFILE_POINT);
     193  #
     194  # Create the transfer point object.
     195  my $trnspnt = new pointObj(); 
     196  #
     197  # Loop through each existing point and recreate it in the new shapefile.
     198  for ($epnt=0; $epnt<$dbfreccnt; $epnt++) {
     199    #
     200    # Get the existing point.
     201    $ecounts->getPoint($epnt,$trnspnt);
     202    #
     203    # Put the point into the new shapefile.
     204    $tcounts->addPoint($trnspnt);
     205  }
     206}
     207 else {
     208  #
     209  # Create the new file.
     210  $tcounts = new shapefileObj("$sfile",$mapscript::MS_SHAPEFILE_POINT);
     211 }
     212#
     213# Create the point object for insertion into the shapefile.
     214my $pnt = new pointObj();
     215#
     216# Loop through each record.
     217#   [0]=dontno1, [1]=tablenamechars, [2]=tablename, [3]=dontno2, [4]=#offields
     218for ($recrd=1; $recrd<$numrecs; $recrd++) {
     219  #
     220  # Create the array for tracking field offsets.
     221  #   To be used later while creating dbf records for each gps point.
     222  my @offsets = ();
     223  #
     224  # Set the initial value for unpacking table name.
     225  $unpackstr = "a38 n";
     226  @recordinfo = unpack($unpackstr, $records[$recrd]->{data});
     227  #
     228  # The character count returned is actual + 1.
     229  $recordinfo[1] = $recordinfo[1] - 1;
     230  #
     231  # Unpack the dontno1 and the table name length.
     232  @recordinfo = unpack($unpackstr, $records[$recrd]->{data});
     233  #
     234  # The character count returned is actual + 1.
     235  $recordinfo[1] = $recordinfo[1] - 1;
     236
     237  #
     238  # Add the remainder of the record info to the unpack string
     239  #   (name, dontno2, #offields).
     240  $unpackstr = $unpackstr . " a$recordinfo[1] a3 n";
     241  #
     242  # Add the first 10 parts of the first field info to the unpack string.
     243  #   All fields appear to have these in common even if they are blank
     244  #    for fields that do not use the particular part.
     245  #   (fieldname, dontno3, datatype, isrange, null, upperlimit, lowerlimit,
     246  #    step, dontno4, fieldsize).
     247  #   [offset+0]=fieldname, [offset+1]=dontno3, [offset+2]=datatype
     248  #   [offset+3]=isrange, [offset+4]=null,
     249  #   [offset+5]=uppperlimit, [offset+6]=lowerlimit, [offset+7]=step,
     250  #   [offset+8]=dontno4, [offset+9]=fieldsize
     251  $unpackstr = $unpackstr . " A19 a10 n n a N N N a14 n";
     252  #
     253  # Set the inital value for the field offset
     254  #   (the number of parts for the previous field(s)).
     255  my $fieldoffset = 0;
     256  #
     257  # Grab the rest of the record info and
     258  #   the field info up to the data length indicator.
     259  ($recordinfo[0], $recordinfo[1], $recordinfo[2], $recordinfo[3], $recordinfo[4], @fieldinfo) = unpack $unpackstr, $records[$recrd]->{data};
     260  #
     261  # The character count returned is actual + 1.
     262  $recordinfo[1] = $recordinfo[1] - 1;
     263  #
     264  #
     265  # Print the record info to see if we got this right.
     266  #print "\nRecord # = $recrd\nNumber of Characters in Table Name = $recordinfo[1]\nTable Name = $recordinfo[2]\nNumber of Fields = $recordinfo[4]\n";
     267  # Loop through each field.
     268  for ($fld=0; $fld<$recordinfo[4]; $fld++) {
     269    #
     270    # The actual field number to print is fld + 1.
     271    my $fldprint = $fld + 1;
     272    #
     273    # Grab the field info up to the data length indicator.
     274    ($recordinfo[0], $recordinfo[1], $recordinfo[2], $recordinfo[3], $recordinfo[4], @fieldinfo) = unpack $unpackstr, $records[$recrd]->{data};
     275    #
     276    # The character count returned is actual + 1.
     277    $recordinfo[1] = $recordinfo[1] - 1;
     278    #
     279    # What is the length of the data.
     280
     281    my $dlength = $fieldinfo[$fieldoffset+9];
     282    #
     283    # The field type comes in strange sometimes so this should truncate it
     284    #   so it only contains values of 0-10.
     285    $fieldinfo[$fieldoffset+2] = 256 * ( ( $fieldinfo[$fieldoffset+2] / 256 ) - ( int( $fieldinfo[$fieldoffset+2] / 256 ) ) );
     286    #
     287    # Okay, the same thing happens with the range.
     288    $fieldinfo[$fieldoffset+3] = 256 * ( ( $fieldinfo[$fieldoffset+3] / 256 ) - ( int( $fieldinfo[$fieldoffset+3] / 256 ) ) );
     289    #
     290    # Add to the unpack string the unpack string for the field type.
     291    $unpackstr = $unpackstr . $ustring[$fieldinfo[$fieldoffset+2]];
     292    #
     293    # For some reason the type appears to be 8-bit instead if 16. So
     294    #   to make sure
     295    #
     296    # Add to the array the rest of the parts for the field.
     297    ($recordinfo[0], $recordinfo[1], $recordinfo[2], $recordinfo[3], $recordinfo[4], @fieldinfo) = unpack $unpackstr, $records[$recrd]->{data};
     298    #
     299    # Escape out any unprintable characters in the field name.
     300    $fieldinfo[$offsets[$iname]+0] = uc($fieldinfo[$offsets[$iname]+0]);
     301    $fieldinfo[$offsets[$iname]+0] =~ s/[^\x41-\x5A]//g;
     302    #
     303    # The field type comes in strange sometimes so this should truncate it
     304    #   so it only contains values of 0-10.
     305    #   (binary/unpack guru applications now being accepted).
     306    # Note: Basically this divides by base 16 to move the number 2 decimal
     307    #       places left then truncates the whole number and multiplies by
     308    #       base 16 to move the decimal 2 places right.
     309    $fieldinfo[$fieldoffset+2] = 256 * ( ( $fieldinfo[$fieldoffset+2] / 256 ) - ( int( $fieldinfo[$fieldoffset+2] / 256 ) ) );
     310    #
     311    # Okay, the same thing happens with the range.
     312    $fieldinfo[$fieldoffset+3] = 256 * ( ( $fieldinfo[$fieldoffset+3] / 256 ) - ( int( $fieldinfo[$fieldoffset+3] / 256 ) ) );
     313    #
     314    # Print the field info to see if we got this right.
     315    #print "Field Offset = $fieldoffset\nField $fldprint Name = $fieldinfo[$fieldoffset+0]\nData Type = $fieldinfo[$fieldoffset+2]\nIsRange = $fieldinfo[$fieldoffset+3]\nUpper Limit = $fieldinfo[$fieldoffset+5]\nLower Limit = $fieldinfo[$fieldoffset+6]\nStep = $fieldinfo[$fieldoffset+7]\nField Size = $fieldinfo[$fieldoffset+9]\n";
     316    #
     317    # How many data parts are there.
     318    #   The total number of field parts - 10 is the number of data parts.
     319    my $dparts = $parts[$fieldinfo[$fieldoffset+2]];
     320    #
     321    # Loop through each of the field value parts.
     322    for ($dpart=10; $dpart<$dparts; $dpart++) {
     323      #
     324      # The actual data part id is the current dpart - 9 (0 thru 9 of the
     325      #   field array).
     326      my $dprint = $dpart - 9;
     327      #
     328      # Print the field info to see if we got this right.
     329      #print "Data Value $dprint = $fieldinfo[$fieldoffset+$dpart]\n";
     330    }
     331    #
     332    # If the field is a location convert the lat/long to state plane.
     333    if (  $fieldinfo[$fieldoffset+2] == 4 ) {
     334      #
     335      # Do the convert.
     336      # Bunches of notes: The projection name is latlong but supply
     337      #   the coordinates as long/lat.
     338      #   The +to section contains units of us-ft but MUST specify
     339      #     false_east(x_0) in meters.
     340      #   An indespensible resource was:
     341      #     http://www.edc.uri.edu/nrs/classes/NRS522/Tools/StatePlaneZones.htm
     342      # Note: If I was smart I would have used the pointObj project method.
     343      system("echo \'$fieldinfo[$fieldoffset+18]d$fieldinfo[$fieldoffset+20].$fieldinfo[$fieldoffset+22]W $fieldinfo[$fieldoffset+11]d$fieldinfo[$fieldoffset+13].$fieldinfo[$fieldoffset+15]N\' | cs2cs +proj=latlong +datum=NAD83 +to +proj=lcc +datum=NAD27 +units=ft +lon_0=-82.5 +lat_0=39.666666667 +lat_1=40.433333333 +lat_2=41.433333333 +x_0=609601.21920 +y_0=0 > /tmp/coordinates");
     344      #
     345      # Open the coordinate file.
     346      open(COORDS,"</tmp/coordinates");
     347      #
     348      # Read the coordinates in.
     349      my @coords = split('\t', <COORDS>);
     350      my @northelev = split(' ',$coords[1]);
     351      #
     352      # Print out the coordinates to see if we have this right.
     353      #print "Easting = $coords[0], Northing = $northelev[0], Elevation = $fieldinfo[$fieldoffset+16]\n";
     354      #
     355      # Close the coordinate file.
     356      close COORDS;
     357      #
     358      # Set the x & y for the point object.
     359      $pnt->{x} = $coords[0];
     360      $pnt->{y} = $northelev[0];
     361      #
     362      # Add the point to the shapefile.
     363      $tcounts->addPoint($pnt);
     364    }
     365    #
     366    # Print the unpack string to see if we got this right.
     367    #print "UnPack String = $unpackstr\n";
     368    #
     369    # Add the next fields standard 10 parts to the unpack string.
     370    $unpackstr = $unpackstr . " A19 a10 n n a N N N a14 n";
     371    #
     372    # Record where this field started at.
     373    $offsets[$fld] = $fieldoffset;
     374    #
     375    # Set the field offset to include the now completed field.
     376    $fieldoffset = $fieldoffset + $parts[$fieldinfo[$fieldoffset+2]];
     377  }
     378  #
     379  # Does the dbf need created and is this the first record.
     380  if ( ( $dbfreccnt == -1 ) && ( $recrd == 1 ) ) {
     381    #
     382
     383    # Set the record count to 0.
     384    $dbfreccnt = 0;
     385    #
     386    # How many fields are there.
     387    my $fldcnt = scalar(@offsets);
     388    #
     389    # Initialize the field names, type, length, & decimal strings to blank.
     390    my $fldnames = '';
     391    my $fldtypes = '';
     392    my $fldlenth = '';
     393    my $flddecml = '';
     394    #
     395    # Loop through each field and concatenate the name, type, length, & decimal together.
     396    for ($iname=0; $iname<$fldcnt; $iname++) {
     397      #
     398      # Escape out any unprintable characters in the field name.
     399      $fieldinfo[$offsets[$iname]+0] = uc($fieldinfo[$offsets[$iname]+0]);
     400      $fieldinfo[$offsets[$iname]+0] =~ s/[^\x41-\x5A]//g;
     401      #
     402      # The field type comes in strange sometimes so this should truncate it
     403      #   so it only contains values of 0-10.
     404      #   (binary/unpack guru applications now being accepted).
     405      # Note: Basically this divides by base 16 to move the number 2 decimal
     406      #       places left then truncates the whole number and multiplies by
     407      #       base 16 to move the decimal 2 places right.
     408      $fieldinfo[$offsets[$iname]+2] = 256 * ( ( $fieldinfo[$offsets[$iname]+2] / 256 ) - ( int( $fieldinfo[$offsets[$iname]+2] / 256 ) ) );
     409      #
     410      # Okay, the same thing happens with the range.
     411      $fieldinfo[$offsets[$iname]+3] = 256 * ( ( $fieldinfo[$offsets[$iname]+3] / 256 ) - ( int( $fieldinfo[$offsets[$iname]+3] / 256 ) ) );
     412      #
     413      # Concatenate the field name.
     414      $fldnames = $fldnames . ' "' . $fieldinfo[$offsets[$iname]+0] . '"';
     415      #
     416      # Concatenate the field types.
     417      $fldtypes = $fldtypes . ' "' . $dbfftype[$fieldinfo[$offsets[$iname]+2]] . '"';
     418      #
     419      # Concatenate the field lengths.
     420      $fldlenth = $fldlenth . ' "' . $dbffsize[$fieldinfo[$offsets[$iname]+2]] . '"';
     421      #
     422      # Concatenate the field decimals.
     423      #   All undef right now.
     424      $flddecml = $flddecml . ' "undef"';
     425      #
     426      # If this is not the last field throw in a comma.
     427      if ( $iname != ( $fldcnt - 1 ) ) {
     428        $fldnames = $fldnames . ',';
     429        $fldtypes = $fldtypes . ',';
     430        $fldlenth = $fldlenth . ',';
     431        $flddecml = $flddecml . ',';
     432      }
     433    }
     434    #
     435    # Add the fields for the record number and error flag.
     436    $fldnames = $fldnames . ', "RECORD", "ERRFLAG"';
     437    $fldtypes = $fldtypes . ', "N", "N"';
     438    $fldlenth = $fldlenth . ', "6", "2"';
     439    $flddecml = $flddecml . ', "undef", "undef"';
     440    #
     441    # Create the xbase call.
     442    my $xbcall = 'XBase->create(name => "' . $sfile . '.dbf", field_names => [' . $fldnames . ' ], field_types => [' . $fldtypes . ' ], field_lengths => [' . $fldlenth . ' ], field_decimals => [' . $flddecml . ' ]) or die XBase->errstr;';
     443    #
     444    # Print out the create line to see if we got this right.
     445    #print "Field Names = $fldnames\nField Types = $fldtypes\nField Sizes = $fldlenth\nField Decimals = $flddecml\n";
     446    #
     447    # Create the dbf file.
     448    $dbh = eval($xbcall);
     449  }
     450  #
     451  # Add the data for this pdb record to the dbf file.
     452  #
     453  # Start the xbase add-record call.
     454  my $xbadd = '$dbh->set_record($dbfreccnt,';
     455  #
     456  # How many fields are there.
     457  my $fldcnt = scalar(@offsets);
     458  #
     459  # Go through each field and concatenate the values together.
     460  for ($iname=0; $iname<$fldcnt; $iname++) {
     461    #
     462    # The field type comes in strange sometimes so this should truncate it
     463    #   so it only contains values of 0-10.
     464    #   (binary/unpack guru applications now being accepted).
     465    # Note: Basically this divides by base 16 to move the number 2 decimal
     466    #       places left then truncates the whole number and multiplies by
     467
     468    #       base 16 to move the decimal 2 places right.
     469    $fieldinfo[$offsets[$iname]+2] = 256 * ( ( $fieldinfo[$offsets[$iname]+2] / 256 ) - ( int( $fieldinfo[$offsets[$iname]+2] / 256 ) ) );
     470    #
     471    # Is this a number type record.
     472    if ( $fieldinfo[$offsets[$iname]+2] == 7 ) {
     473      $xbadd = $xbadd . $fieldinfo[$offsets[$iname]+10];
     474    }
     475    #
     476    # Is this a location type record.
     477    if ( $fieldinfo[$offsets[$iname]+2] == 4 ) {
     478      $xbadd = $xbadd . '"' . $fieldinfo[$offsets[$iname]+18] . 'd' . $fieldinfo[$offsets[$iname]+20] . '.' . $fieldinfo[$offsets[$iname]+22] . 'W,' . $fieldinfo[$offsets[$iname]+11] . 'd' . $fieldinfo[$offsets[$iname]+13] . '.' . $fieldinfo[$offsets[$iname]+15] . 'N,' . $fieldinfo[$offsets[$iname]+16] . '"';
     479    }
     480    #
     481        # If this is not the last field throw in a comma.
     482    if ( $iname != ( $fldcnt - 1 ) ) {
     483      $xbadd = $xbadd . ',';
     484
     485    }
     486  }
     487  #
     488  # Add the closer to the end of the xbase add-record call.
     489  $xbadd = $xbadd . ', ' . $dbfreccnt . ', 0);';
     490  #
     491  # Print the xbase add-record line to see if we got this right.
     492  #print "$xbadd\n";
     493  #
     494  # Add the record to the dbf file.
     495  eval($xbadd);
     496  #
     497  # Increment the record counter.
     498  $dbfreccnt = $dbfreccnt + 1;
     499}
     500#
     501# Close the new shapefile.
     502undef $tcounts;
     503#
     504# Close the dbf handle/file.
     505undef $dbh;
     506#
     507# Get rid of temporary shapefiles if needed.
     508if ( -e "thistemp.shp" ) {
     509  unlink "thistemp.shp";
     510  unlink "thistemp.shx";
     511  unlink "thistemp.dbf";
     512}
     513}}}