Changes between Initial Version and Version 1 of PerlMapScriptExamples35ex9


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

--

Legend:

Unmodified
Added
Removed
Modified
  • PerlMapScriptExamples35ex9

    v1 v1  
     1= tcheck.pl =
     2
     3The cl.tar.gz url is http://www.highwayengineer.co.medina.oh.us/cl.tar.gz , unfortunately it is large for modem download but hopefully it will be useful to other users.
     4{{{
     5#!perl
     6#!/usr/bin/perl
     7#
     8# Copyright (C) 2002, Lowell Filak.
     9# You may distribute this file under the terms of the Artistic
     10# License.
     11#
     12# Given the name of an existing point shapefile, the name of an existing
     13#   line shapefile, a radius distance, a field name in the point shapefile,
     14#   a filed name in the line shapefile, & optionally an accuracy field name
     15#   in the point shapefile this routine will search within a circular buffer
     16#   of each point (of specified radius + optional accuracy) for a line with a matching
     17#   field value and then assign the point a 0 error for a single match. Otherwise the point
     18#   will be assigned an error of 1 for no matches & x for a number of
     19#   matches > 1 where x is the number of matches.
     20#   Note: Currently the field matching portion contains syntax to reduce
     21#         the value of the fields to numeric before matching.
     22
     23#         The routine only works if both shapefiles are in the current dir.
     24#         The routine requires a RECORD field in the point & line shapefiles that
     25#           reflects the number of the record (this must line up) with the
     26#           shape number of the point. First record number being 0.
     27#         Instead of using the xbase module directly like find.pl this
     28#           routine uses the sql style interface to the shapefile attributes.
     29#           Why? Because it is a lot easier to code.
     30#         The routine requires a field named errflag to exist in the point
     31#           shapefile (dbf).
     32#         The errflag field created by tcounts.pl is limited to a max of 99.
     33#           This means that the point buffer should be small enough not to
     34#           overlap more than 99 lines.
     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#   DBI (cpan), DBD::XBase (cpan),
     40#   & XBase (cpan).
     41#   Please run tcounts.pl first then:
     42#     please download cl.tar.gz also, and:
     43#     tar -xf cl.tar.gz --ungzip
     44#
     45# Suggested run line = ./tcheck.pl -pfile=traffic -lfile=cl -iradius=35 -pfield=roadnumber -lfield=first_ -afield=accuracyt
     46#
     47# Include the mapscript module.
     48use mapscript;
     49#
     50# Include the xbase and dbi modules for searching and updating values.
     51use XBase;
     52use DBI;
     53#
     54# Include the getopt module to read input.
     55use Getopt::Long;
     56#
     57# Grab the file name from the input.
     58&GetOptions('pfile=s' => \$pfile, 'lfile=s' => \$lfile, 'iradius=i' => \$iradius, 'pfield=s' => \$pfield, 'lfield=s' => \$lfield, 'afield=s' => $afield);
     59if ( !$pfile || !$lfile || !$iradius || !$pfield || !$lfield) {
     60  print "Syntax: tcheck.pl -pfile=[in_point_shapefile_name] -lfile=[in_line_shapefile_name] -iradius=[search_radius] -pfield=[point_shapefile_field_name] -lfield=[line_shapefiel_field_name] -afield={optional_accuracy_field_name_in_point_shapefile";
     61  exit 0;
     62}
     63#
     64# Create a unique name for a new mapfile for querying the road centerlines.
     65#
     66# Grab the date.
     67my ($sec,$min,$hr,$mdy,$mnth,$yr,$wdy,$ydy,$isdst) = localtime;
     68#
     69# Grab the process id.
     70my $spid = $$;
     71#
     72# Create the name & make sure it is no longer than 8 characters.
     73my $sfile = "T$hr$min$sec$spid";
     74#my $sfile = "TEST";
     75#
     76# Open the mapfile for writing.
     77open(MAPFILE, ">$sfile.map");
     78#
     79# Open the existing point shapefile.
     80my $inshpf = new shapefileObj($pfile, -1) or die "Unable to open shapefile $pfile.";
     81#
     82# What are the extents.
     83my $inshpminx = $inshpf->{bounds}->{minx};
     84my $inshpminy = $inshpf->{bounds}->{miny};
     85my $inshpmaxx = $inshpf->{bounds}->{maxx};
     86my $inshpmaxy = $inshpf->{bounds}->{maxy};
     87#
     88# Create the contents of the mapfile.
     89print MAPFILE <<EOF;
     90#
     91NAME $sfile
     92STATUS ON
     93SIZE 600 600
     94SYMBOLSET "$sfile.sym"
     95EXTENT $inshpminx $inshpminy $inshpmaxx $inshpmaxy
     96UNITS FEET
     97SHAPEPATH ""
     98IMAGECOLOR 255 255 255
     99LAYER
     100  NAME centerline
     101  TYPE LINE
     102  STATUS ON
     103  DATA "$lfile"
     104  TEMPLATE 'bogus.html'
     105  CLASS
     106    COLOR 255 0 0
     107    NAME "Centerline"
     108  END
     109END
     110END
     111EOF
     112#
     113# Close the mapfile.
     114close MAPFILE;
     115#
     116# Open the symbol file for writing.
     117open(SYMFILE, ">$sfile.sym");
     118#
     119# Create the contents of the symbol file.
     120print SYMFILE <<EOF;
     121SYMBOLSET
     122END
     123EOF
     124#
     125# Close the symbol file.
     126close SYMFILE;
     127#
     128# How many points are there.
     129my $innumshp = $inshpf->{numshapes};
     130#
     131# Connect to the dbf files as if they were an rdbms.
     132$dbf=DBI->connect("dbi:XBase:.");
     133#
     134# Create a circle shape.
     135#   Thanks to Tim Mackey.
     136
     137#
     138# Create a point object to hold the line-segment increments.
     139my $p=new pointObj();
     140#
     141# Set the value for pi.
     142my $pi=3.141592654;
     143#
     144# Create the mapfileobject.
     145my $map = new mapObj("$sfile.map") or die("Unable to Open Default MapFile $sfile.map!");
     146#
     147# Create the layer object for the later queries.
     148my $lyr = $map->getLayerByName("centerline") or die('Unable to Open Centerline Layer!');
     149#
     150
     151# Set the default query result to blank.
     152my @row = ();
     153#
     154# Set the starting radius to 0.
     155my $radius = 0;
     156#
     157# Create the point holder.
     158my $inpnt = new pointObj();
     159#
     160# Loop through each point.
     161for ($inpntnum=0; $inpntnum<$innumshp; $inpntnum++ ) {
     162  print "Checking Point #$inpntnum...\n";
     163  #
     164  # Grab the point by number.
     165  my $junk = $inshpf->getPoint($inpntnum, $inpnt);
     166  #
     167  # Set the default accuracy to 0.
     168  my $accuracy = 0;
     169
     170  #
     171  # If the accuracy field is specified use it.
     172  if ( $afield ) {
     173    #
     174    # Retrieve the accuracy distance for this point by point number.
     175    $sth = $dbf->prepare("SELECT $afield FROM $pfile WHERE record = $inpntnum");
     176    $sth->execute;
     177    #
     178    # Grab the result of the query.
     179    @row = $sth->fetchrow_array;
     180    #
     181    # Set the value for the accuracy.
     182    $accuracy = $row[0];
     183  }
     184  #
     185  # Set the value for the radius.
     186  $radius = $accuracy + $iradius;
     187  #
     188  # What is the field value for this point.
     189  $sth = $dbf->prepare("SELECT $pfield FROM $pfile WHERE record = $inpntnum");
     190  $sth->execute;
     191
     192  #
     193  # Grab the result of the query.
     194  @row = $sth->fetchrow_array;
     195  #
     196  # Set the value for the field.
     197  $pfldval = $row[0];
     198  #
     199  # Set the coordinates of the center of the circle to the coordinates of the
     200  #   existing point.
     201  my $x = $inpnt->{x};
     202  my $y = $inpnt->{y};
     203  #
     204  # Create a line object to hold the bounds of the shape.
     205  my $line=new lineObj();
     206  #
     207  # Create the circle shapeobject.
     208  my $circle=new shapeObj($mapscript::MS_SHAPE_POLYGON);
     209  #
     210  # Loop through the segments of the circle.
     211  for($i=0;$i<2.1*$pi;$i=$i+$pi/100) {
     212    #
     213    # The x & y coordinates of the segment point.
     214    $p->{x} = $x + $radius * cos($i);
     215    $p->{y} = $y + $radius * sin($i);
     216    #
     217    # Add the point to the line.
     218    $line->add($p);
     219    #
     220    # Set a marker for the first/last point if this is the first point.
     221    # This is not needed. See note below.
     222    #if ( $i==0 ) {
     223      #$firstpntx = $p->{x};
     224      #$firstpnty = $p->{y};
     225    #}
     226  }
     227  #
     228  # Throw in the closing point.
     229  # This must not be needed. The original code never added this
     230  #   point to the line but yet the shape closes.?
     231  #$p->{x} = $firstpntx;
     232  #$p->{y} = $firstpnty;
     233  #
     234  # Add the line to form the circle shape.
     235  $circle->add($line);
     236  #
     237  # Clear out the line object.
     238  undef $line;
     239  #
     240  # Query the line layer with this shape.
     241  $lyr->queryByShape($map, $circle);
     242  #
     243  # Clear out the circle object.
     244  undef $circle;
     245  #
     246  # How many matches found.
     247  #
     248  # Create a resultcache object to see how many results.
     249  my $rsltcache = $lyr->{resultcache};
     250  #
     251  # How many matches did we find.
     252  my $numrslt = $rsltcache->{numresults};
     253  #
     254  # Clear the resultcache.
     255  undef $rsltcache;
     256  #
     257  # if there is no match then mark point with error of 1.
     258  if ( $numrslt == 0 ) {
     259    $sth = $dbf->prepare("UPDATE $pfile SET errflag = 1 WHERE record = $inpntnum");
     260    $sth->execute;
     261  }
     262  #
     263  # Set the match quantity to the number of possible matches to start with.
     264  my $possmatches = $numrslt;
     265  #
     266  # Loop through each match to see if the field value matches the point.
     267  for ($line=0; $line<$numrslt; $line++) {
     268    #
     269    # Grab the nth result of the query.
     270    $resultmember = $lyr->getResult($line);   
     271    #
     272    # What is the shape number of this possible match.
     273    $shaperecnum = $resultmember->{shapeindex};
     274    #
     275    # Query the line attribute to see if the fields match.
     276    $sth = $dbf->prepare("SELECT $lfield FROM $lfile WHERE record = $shaperecnum");
     277    $sth->execute;
     278    #
     279    # Grab the result of the query.
     280
     281    @row = $sth->fetchrow_array;
     282    #
     283    # Set the value of the field.
     284    $lfldval = $row[0];
     285    #
     286    # Take out anything that isn't an integer.
     287    $lfldval =~ s/[^\x30-\x39]//g;
     288    #
     289    # Does this line value match the point value.
     290    if ( $pfldval == $lfldval ) {
     291      #
     292      # Yes it does.
     293      #
     294      # I propose to do nothing.
     295    }
     296     else {
     297      #
     298      # Nope.
     299      #
     300      # Subtract one from the possible matches.
     301      $possmatches = $possmatches - 1;
     302     }
     303  }
     304  #
     305  # If there is no match found then mark point with error.
     306  if ( $possmatches == 0 ) {
     307    #
     308    # Mark with a 1.
     309    print "\tNO Match Found...\n";
     310    $sth = $dbf->prepare("UPDATE $pfile SET errflag = 1 WHERE record = $inpntnum");
     311    $sth->execute;
     312  }
     313  elsif ( $possmatches > 1 ) {
     314    #
     315    # Mark with the number of matches.
     316    print "\t$possmatches Matches Found...\n";
     317    $sth = $dbf->prepare("UPDATE $pfile SET errflag = $possmatches WHERE record = $inpntnum");
     318    $sth->execute;
     319  }
     320  elsif ( $possmatches == 1 ) {
     321    #
     322    # Make sure the error is set to 0 just incase the routine was run multiple
     323    #   times on the same shapefile with different radii.
     324    print "\tOne Match Found...\n";
     325    $sth = $dbf->prepare("UPDATE $pfile SET errflag = 0 WHERE record = $inpntnum");
     326    $sth->execute;
     327  }
     328}   
     329#
     330# Get rid of the temporary mapfile & symbol file.
     331unlink "$sfile.map";
     332
     333unlink "$sfile.sym";
     334}}}
     335----
     336back to PerlMapScript