-
Notifications
You must be signed in to change notification settings - Fork 2
PerlMapScriptExamples35ex8
{{{
#!perl
#!/usr/bin/perl
Palm (p5-Palm-1.1.5 http://theoryx5.uwinnipeg.ca/CPAN/data/p5-Palm/Palm/Raw.html),
Additional requirements are: a working pilot-xfer (pilot-link http://www.pilot-link.org/)
command working (www.remotesensing.org/proj/).
Hills Innovations-Garmin2Palm cable (http://www.blue-hills-innovations.com).
use Palm::PDB;
use Palm::Raw;
use mapscript;
use XBase;
use Getopt::Long;
&GetOptions('pdbfile=s' => $pdbfile, 'sfile=s' => $sfile);
if ( !$sfile ) {
($sec,$min,$hr,$mdy,$mnth,$yr,$wdy,$ydy,$isdst) = localtime;
$sfile = substr("$hr$min$sec$spid", -8);
}
if( !$pdbfile ) {
system("pilot-xfer -f StickeDB");
$pdbfile = 'StickeDB';
}
my $pdb = new Palm::PDB;
$pdb->Load("$pdbfile.pdb");
my @records = @{$pdb->{records}};
my $numrecs = scalar(@records);
my @types = ('bearing', 'boolean', 'date', 'textline', 'location', 'unused', 'notepad', 'number', 'picklist', 'subnote', 'time');
my @parts = (0,0,0,0,23,0,0,11,0,0,0);
my @ustring = (' a*', ' a*', ' a*', ' a*', ' l n a2 n a2 n s a2 n a2 n a2 n', ' a*', ' a*', ' N', ' a*', ' a*', ' a*');
my @dbfftype = ('C', 'L', 'D', 'C', 'C', 'C', 'C', 'N', 'C', 'C', 'N');
my @dbffsize = ('255', '1', '8', '255', '31', '0', '255', '11', '255', '255', '10');
my @offsets = ();
my $unpackstr = "";
my @recordinfo = ();
my $dbfreccnt = 0;
if ( -e "$sfile.dbf" ) {
$dbh = new XBase "$sfile.dbf" or die XBase->errstr;
$dbfreccnt = $dbh->last_record + 1;
}
else {
$dbfreccnt = -1;
}
if ( -e "$sfile.shp" ) {
system("mv $sfile.shp thistemp.shp; mv $sfile.shx thistemp.shx; touch thistemp.dbf");
$ecounts = new shapefileObj("thistemp",-1);
$tcounts = new shapefileObj("$sfile",$mapscript::MS_SHAPEFILE_POINT);
my $trnspnt = new pointObj();
for ($epnt=0; $epnt<$dbfreccnt; $epnt++) {
#
# Get the existing point.
$ecounts->getPoint($epnt,$trnspnt);
#
# Put the point into the new shapefile.
$tcounts->addPoint($trnspnt);
}
}
else {
$tcounts = new shapefileObj("$sfile",$mapscript::MS_SHAPEFILE_POINT);
}
my $pnt = new pointObj();
for ($recrd=1; $recrd<$numrecs; $recrd++) {
my @offsets = ();
$unpackstr = "a38 n";
@recordinfo = unpack($unpackstr, $records[$recrd]->{data});
$recordinfo[1] = $recordinfo[1] - 1;
@recordinfo = unpack($unpackstr, $records[$recrd]->{data});
$recordinfo[1] = $recordinfo[1] - 1;
$unpackstr = $unpackstr . " a$recordinfo[1] a3 n";
$unpackstr = $unpackstr . " A19 a10 n n a N N N a14 n";
my $fieldoffset = 0;
($recordinfo[0], $recordinfo[1], $recordinfo[2], $recordinfo[3], $recordinfo[4], @fieldinfo) = unpack $unpackstr, $records[$recrd]->{data};
$recordinfo[1] = $recordinfo[1] - 1;
#print "\nRecord # = $recrd\nNumber of Characters in Table Name = $recordinfo[1]\nTable Name = $recordinfo[2]\nNumber of Fields = $recordinfo[4]\n";
for ($fld=0; $fld<$recordinfo[4]; $fld++) {
#
# The actual field number to print is fld + 1.
my $fldprint = $fld + 1;
#
# Grab the field info up to the data length indicator.
($recordinfo[0], $recordinfo[1], $recordinfo[2], $recordinfo[3], $recordinfo[4], @fieldinfo) = unpack $unpackstr, $records[$recrd]->{data};
#
# The character count returned is actual + 1.
$recordinfo[1] = $recordinfo[1] - 1;
#
# What is the length of the data.
my $dlength = $fieldinfo[$fieldoffset+9];
#
# The field type comes in strange sometimes so this should truncate it
# so it only contains values of 0-10.
$fieldinfo[$fieldoffset+2] = 256 * ( ( $fieldinfo[$fieldoffset+2] / 256 ) - ( int( $fieldinfo[$fieldoffset+2] / 256 ) ) );
#
# Okay, the same thing happens with the range.
$fieldinfo[$fieldoffset+3] = 256 * ( ( $fieldinfo[$fieldoffset+3] / 256 ) - ( int( $fieldinfo[$fieldoffset+3] / 256 ) ) );
#
# Add to the unpack string the unpack string for the field type.
$unpackstr = $unpackstr . $ustring[$fieldinfo[$fieldoffset+2]];
#
# For some reason the type appears to be 8-bit instead if 16. So
# to make sure
#
# Add to the array the rest of the parts for the field.
($recordinfo[0], $recordinfo[1], $recordinfo[2], $recordinfo[3], $recordinfo[4], @fieldinfo) = unpack $unpackstr, $records[$recrd]->{data};
#
# Escape out any unprintable characters in the field name.
$fieldinfo[$offsets[$iname]+0] = uc($fieldinfo[$offsets[$iname]+0]);
$fieldinfo[$offsets[$iname]+0] =~ s/[^\x41-\x5A]//g;
#
# The field type comes in strange sometimes so this should truncate it
# so it only contains values of 0-10.
# (binary/unpack guru applications now being accepted).
# Note: Basically this divides by base 16 to move the number 2 decimal
# places left then truncates the whole number and multiplies by
# base 16 to move the decimal 2 places right.
$fieldinfo[$fieldoffset+2] = 256 * ( ( $fieldinfo[$fieldoffset+2] / 256 ) - ( int( $fieldinfo[$fieldoffset+2] / 256 ) ) );
#
# Okay, the same thing happens with the range.
$fieldinfo[$fieldoffset+3] = 256 * ( ( $fieldinfo[$fieldoffset+3] / 256 ) - ( int( $fieldinfo[$fieldoffset+3] / 256 ) ) );
#
# Print the field info to see if we got this right.
#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";
#
# How many data parts are there.
# The total number of field parts - 10 is the number of data parts.
my $dparts = $parts[$fieldinfo[$fieldoffset+2]];
#
# Loop through each of the field value parts.
for ($dpart=10; $dpart<$dparts; $dpart++) {
#
# The actual data part id is the current dpart - 9 (0 thru 9 of the
# field array).
my $dprint = $dpart - 9;
#
# Print the field info to see if we got this right.
#print "Data Value $dprint = $fieldinfo[$fieldoffset+$dpart]\n";
}
#
# If the field is a location convert the lat/long to state plane.
if ( $fieldinfo[$fieldoffset+2] == 4 ) {
#
# Do the convert.
# Bunches of notes: The projection name is latlong but supply
# the coordinates as long/lat.
# The +to section contains units of us-ft but MUST specify
# false_east(x_0) in meters.
# An indespensible resource was:
# http://www.edc.uri.edu/nrs/classes/NRS522/Tools/StatePlaneZones.htm
# Note: If I was smart I would have used the pointObj project method.
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");
#
# Open the coordinate file.
open(COORDS,"</tmp/coordinates");
#
# Read the coordinates in.
my @coords = split('\t', <COORDS>);
my @northelev = split(' ',$coords[1]);
#
# Print out the coordinates to see if we have this right.
#print "Easting = $coords[0], Northing = $northelev[0], Elevation = $fieldinfo[$fieldoffset+16]\n";
#
# Close the coordinate file.
close COORDS;
#
# Set the x & y for the point object.
$pnt->{x} = $coords[0];
$pnt->{y} = $northelev[0];
#
# Add the point to the shapefile.
$tcounts->addPoint($pnt);
}
#
# Print the unpack string to see if we got this right.
#print "UnPack String = $unpackstr\n";
#
# Add the next fields standard 10 parts to the unpack string.
$unpackstr = $unpackstr . " A19 a10 n n a N N N a14 n";
#
# Record where this field started at.
$offsets[$fld] = $fieldoffset;
#
# Set the field offset to include the now completed field.
$fieldoffset = $fieldoffset + $parts[$fieldinfo[$fieldoffset+2]];
}
if ( ( $dbfreccnt == -1 ) && ( $recrd == 1 ) ) {
#
# Set the record count to 0.
$dbfreccnt = 0;
#
# How many fields are there.
my $fldcnt = scalar(@offsets);
#
# Initialize the field names, type, length, & decimal strings to blank.
my $fldnames = '';
my $fldtypes = '';
my $fldlenth = '';
my $flddecml = '';
#
# Loop through each field and concatenate the name, type, length, & decimal together.
for ($iname=0; $iname<$fldcnt; $iname++) {
#
# Escape out any unprintable characters in the field name.
$fieldinfo[$offsets[$iname]+0] = uc($fieldinfo[$offsets[$iname]+0]);
$fieldinfo[$offsets[$iname]+0] =~ s/[^\x41-\x5A]//g;
#
# The field type comes in strange sometimes so this should truncate it
# so it only contains values of 0-10.
# (binary/unpack guru applications now being accepted).
# Note: Basically this divides by base 16 to move the number 2 decimal
# places left then truncates the whole number and multiplies by
# base 16 to move the decimal 2 places right.
$fieldinfo[$offsets[$iname]+2] = 256 * ( ( $fieldinfo[$offsets[$iname]+2] / 256 ) - ( int( $fieldinfo[$offsets[$iname]+2] / 256 ) ) );
#
# Okay, the same thing happens with the range.
$fieldinfo[$offsets[$iname]+3] = 256 * ( ( $fieldinfo[$offsets[$iname]+3] / 256 ) - ( int( $fieldinfo[$offsets[$iname]+3] / 256 ) ) );
#
# Concatenate the field name.
$fldnames = $fldnames . ' "' . $fieldinfo[$offsets[$iname]+0] . '"';
#
# Concatenate the field types.
$fldtypes = $fldtypes . ' "' . $dbfftype[$fieldinfo[$offsets[$iname]+2]] . '"';
#
# Concatenate the field lengths.
$fldlenth = $fldlenth . ' "' . $dbffsize[$fieldinfo[$offsets[$iname]+2]] . '"';
#
# Concatenate the field decimals.
# All undef right now.
$flddecml = $flddecml . ' "undef"';
#
# If this is not the last field throw in a comma.
if ( $iname != ( $fldcnt - 1 ) ) {
$fldnames = $fldnames . ',';
$fldtypes = $fldtypes . ',';
$fldlenth = $fldlenth . ',';
$flddecml = $flddecml . ',';
}
}
#
# Add the fields for the record number and error flag.
$fldnames = $fldnames . ', "RECORD", "ERRFLAG"';
$fldtypes = $fldtypes . ', "N", "N"';
$fldlenth = $fldlenth . ', "6", "2"';
$flddecml = $flddecml . ', "undef", "undef"';
#
# Create the xbase call.
my $xbcall = 'XBase->create(name => "' . $sfile . '.dbf", field_names => [' . $fldnames . ' ], field_types => [' . $fldtypes . ' ], field_lengths => [' . $fldlenth . ' ], field_decimals => [' . $flddecml . ' ]) or die XBase->errstr;';
#
# Print out the create line to see if we got this right.
#print "Field Names = $fldnames\nField Types = $fldtypes\nField Sizes = $fldlenth\nField Decimals = $flddecml\n";
#
# Create the dbf file.
$dbh = eval($xbcall);
}
my $xbadd = '$dbh->set_record($dbfreccnt,';
my $fldcnt = scalar(@offsets);
for ($iname=0; $iname<$fldcnt; $iname++) {
#
# The field type comes in strange sometimes so this should truncate it
# so it only contains values of 0-10.
# (binary/unpack guru applications now being accepted).
# Note: Basically this divides by base 16 to move the number 2 decimal
# places left then truncates the whole number and multiplies by
# base 16 to move the decimal 2 places right.
$fieldinfo[$offsets[$iname]+2] = 256 * ( ( $fieldinfo[$offsets[$iname]+2] / 256 ) - ( int( $fieldinfo[$offsets[$iname]+2] / 256 ) ) );
#
# Is this a number type record.
if ( $fieldinfo[$offsets[$iname]+2] == 7 ) {
$xbadd = $xbadd . $fieldinfo[$offsets[$iname]+10];
}
#
# Is this a location type record.
if ( $fieldinfo[$offsets[$iname]+2] == 4 ) {
$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] . '"';
}
#
# If this is not the last field throw in a comma.
if ( $iname != ( $fldcnt - 1 ) ) {
$xbadd = $xbadd . ',';
}
}
$xbadd = $xbadd . ', ' . $dbfreccnt . ', 0);';
#print "$xbadd\n";
eval($xbadd);
$dbfreccnt = $dbfreccnt + 1;
}
undef $tcounts;
undef $dbh;
if ( -e "thistemp.shp" ) {
unlink "thistemp.shp";
unlink "thistemp.shx";
unlink "thistemp.dbf";
}
}}}
back to PerlMapScrip