| 1 | = tcheck.pl = |
| 2 | |
| 3 | The 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. |
| 48 | use mapscript; |
| 49 | # |
| 50 | # Include the xbase and dbi modules for searching and updating values. |
| 51 | use XBase; |
| 52 | use DBI; |
| 53 | # |
| 54 | # Include the getopt module to read input. |
| 55 | use 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); |
| 59 | if ( !$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. |
| 67 | my ($sec,$min,$hr,$mdy,$mnth,$yr,$wdy,$ydy,$isdst) = localtime; |
| 68 | # |
| 69 | # Grab the process id. |
| 70 | my $spid = $$; |
| 71 | # |
| 72 | # Create the name & make sure it is no longer than 8 characters. |
| 73 | my $sfile = "T$hr$min$sec$spid"; |
| 74 | #my $sfile = "TEST"; |
| 75 | # |
| 76 | # Open the mapfile for writing. |
| 77 | open(MAPFILE, ">$sfile.map"); |
| 78 | # |
| 79 | # Open the existing point shapefile. |
| 80 | my $inshpf = new shapefileObj($pfile, -1) or die "Unable to open shapefile $pfile."; |
| 81 | # |
| 82 | # What are the extents. |
| 83 | my $inshpminx = $inshpf->{bounds}->{minx}; |
| 84 | my $inshpminy = $inshpf->{bounds}->{miny}; |
| 85 | my $inshpmaxx = $inshpf->{bounds}->{maxx}; |
| 86 | my $inshpmaxy = $inshpf->{bounds}->{maxy}; |
| 87 | # |
| 88 | # Create the contents of the mapfile. |
| 89 | print MAPFILE <<EOF; |
| 90 | # |
| 91 | NAME $sfile |
| 92 | STATUS ON |
| 93 | SIZE 600 600 |
| 94 | SYMBOLSET "$sfile.sym" |
| 95 | EXTENT $inshpminx $inshpminy $inshpmaxx $inshpmaxy |
| 96 | UNITS FEET |
| 97 | SHAPEPATH "" |
| 98 | IMAGECOLOR 255 255 255 |
| 99 | LAYER |
| 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 |
| 109 | END |
| 110 | END |
| 111 | EOF |
| 112 | # |
| 113 | # Close the mapfile. |
| 114 | close MAPFILE; |
| 115 | # |
| 116 | # Open the symbol file for writing. |
| 117 | open(SYMFILE, ">$sfile.sym"); |
| 118 | # |
| 119 | # Create the contents of the symbol file. |
| 120 | print SYMFILE <<EOF; |
| 121 | SYMBOLSET |
| 122 | END |
| 123 | EOF |
| 124 | # |
| 125 | # Close the symbol file. |
| 126 | close SYMFILE; |
| 127 | # |
| 128 | # How many points are there. |
| 129 | my $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. |
| 139 | my $p=new pointObj(); |
| 140 | # |
| 141 | # Set the value for pi. |
| 142 | my $pi=3.141592654; |
| 143 | # |
| 144 | # Create the mapfileobject. |
| 145 | my $map = new mapObj("$sfile.map") or die("Unable to Open Default MapFile $sfile.map!"); |
| 146 | # |
| 147 | # Create the layer object for the later queries. |
| 148 | my $lyr = $map->getLayerByName("centerline") or die('Unable to Open Centerline Layer!'); |
| 149 | # |
| 150 | |
| 151 | # Set the default query result to blank. |
| 152 | my @row = (); |
| 153 | # |
| 154 | # Set the starting radius to 0. |
| 155 | my $radius = 0; |
| 156 | # |
| 157 | # Create the point holder. |
| 158 | my $inpnt = new pointObj(); |
| 159 | # |
| 160 | # Loop through each point. |
| 161 | for ($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. |
| 331 | unlink "$sfile.map"; |
| 332 | |
| 333 | unlink "$sfile.sym"; |
| 334 | }}} |
| 335 | ---- |
| 336 | back to PerlMapScript |