Skip to content

PerlMapScriptExamples35ex7

Thomas Bonfort edited this page Apr 6, 2012 · 2 revisions

{{{
#!perl
#!/usr/bin/perl -w

Copyright (C) 2002, Lowell Filak.

You may distribute this file under the terms of the Artistic

License.

Given a shapefile name, a coordinate, & an item name

this routine will select a shape,

select all the shapes with the same item value, & create a shapefile of

the selected shape(s).

Required modules are mapscript (installed as part of make install),

Getopt (normally included with Perl) & XBase.

Please download boundary.tar.gz also, and:

tar -xf boundary.tar.gz --ungzip

Note: The suggested run assumes a pick point on a 600x600 image.

Suggested run method = ./qry_point.pl -file=boundary -coorx=372 -coory=115 -item=loc_name

use mapscript;
use Getopt::Long;
use XBase;

Retrieve the input values.

GetOptions('file=s' => $file, 'coorx=s' => $coorx, 'coory=s' => $coory, 'item=s' => $item);

Check the input values.

if ( (!$file) || (!$coorx) || (!$coory) || (!$item) ) {
print "Syntax: find.pl -file=[filename] -coorx=[x_coordinate] -coory=[y_coordinate] -item=[item_name]\n";
exit 0;
}

Upcase the item name.

$item = uc $item;

Create mapfile name.

my $mapfile = $file . '.map';

Open map using default map file.

my $map = new mapObj("$mapfile") or die('Unable to Open Default MapFile!');

Subtract one pixel.

Why is this done?: I don't directly recall but I think it has to do with

mapextents starting at a positive integer while image starts at 0,0.

$imgx = $map->{width} - 1;
$imgy = $map->{height} - 1;

Find the extents of the map.

$minx = $map->{extent}->{minx};
$miny = $map->{extent}->{miny};
$maxx = $map->{extent}->{maxx};
$maxy = $map->{extent}->{maxy};

Caculate a delta x & delta y.

$dx = $maxx - $minx;
$dy = $maxy - $miny;

Divide delta x & y by pixel extents to find factor x & y.

$fctrx = $dx / $imgx;
$fctry = $dy / $imgy;

Adjust to real world coordinates.

$coorx = $coorx * $fctrx;
$coory = $coory * $fctry;
$coorx = $coorx + $minx;
$coory = $maxy - $coory;

Create point object for pick query.

$pnt = new pointObj();
$pnt->{x} = $coorx;
$pnt->{y} = $coory;

Print the point coordinates.

print "Selecting Using Point Coordinates: x=$coorx y=$coory\n";

Get layer for boundary query.

Note: Most of this is already set in the mapfile and is here for sample

only.

my $lyr = $map->getLayerByName("$file") or die('Unable to Open Boundary Layer!');
$lyr->{status} = $mapscript::MS_ON;
$lyr->{type} = $mapscript::MS_LAYER_POLYGON;
$lyr->{data} = "$file";

Query the layer using the created point.

$lyr->queryByPoint($map,$pnt,$mapscript::MS_SINGLE,0);

Create a resultcache object to see how many results.

my $rsltcache = $lyr->{resultcache};

How many matches did we find.

print "Found $rsltcache->{numresults} Result.\n";

Grab the first result (there should only be one).

my $rslt = $lyr->getResult(0);

What is the shape number.

my $record = $rslt->{shapeindex};

Print the shape number.

print "The Query Found Shape #$record.\n";

Query the dbf for the item matching records.

Note: The routine is written to utilize dbf files as they originally are.

Normally you would want to at least add a record number field to the

dbf file so you could use the DBI & DBD::XBase modules to query

the db. You could also load the dbf data into an dbms and use

the DBI & DBD::x modules to query once the record number field

exists.

Open the db handle.

my $dbh = new XBase "$file" or die XBase->errstr;

What is the number of the key field.

my @names = $dbh->field_names;

How many fields are there.

my $fldcnt = $dbh->last_field;

Set the field number to initially 0.

my $fieldnum = 0;

Loop through the fields and find the one we want.

for ($field=0; $field<=$fldcnt; $field++){

Is this the field we were looking for.

if ( $names[$field] eq $item ) {
#
# If so then exit loop.
$fieldnum = $field;
#
# Print the field number.
print "The Key Item is Field #$fieldnum.\n";
last;
}
else {
#
# Fall through.
}
}

Grab the key record & the key item value.

my @row = $dbh->get_record_nf($record, $fieldnum) or die $dbh->errstr;

What is the value for the key item.

my $value = $row[1];

Print the key item value for the key record.

print "The Value of $item for Shape #$record = $value.\n";

Start the number of results at 0.

my $results = 0;

Open the selection set shapefile.

Note: There is a way to obtain a selection set without saving to a

shapefile, however due to the type of data I am accustomed to,

by writing to a shapefile, a type of shapefile cache can be setup.

By naming all shapefiles in a particular directory in a way

that allows them to be reopened for any repetitious queries

the actual work of the query can be bypassed.

my $shapesel = new shapefileObj('selected',$mapscript::MS_SHAPEFILE_POLYGON);

Open the existing shapefile for grabbing the found shapes out of.

my $shapefile = new shapefileObj("$file",-1);

Loop through each record (there are experimental modules for using indexes

available according to xbase man page).

for ($record=0; $record<$dbh->last_record; $record++){

Grab the record.

my @row = $dbh->get_record($record, "$item") or die $dbh->errstr;

Is the record marked for deletion.

my $deleted = $row[0];
if ( $deleted == 1 ) {
#
# If so then skip it.
next;
}
else {
#
# Fall through.
}

Set the value for the search field.

my $fndvalue = $row[1];

Does the value from the field match the value for the key record.

if ( "$fndvalue" ne "$value" ) {
#
# If not skip it.
next;
}
else {
#
# Fall through.
}

Print the found record information.

print "Record #$record Matches with a Value of $fndvalue - good thing :-)\n";

Increment the results counter.

$results = $results + 1;

Create the shape object for holding the found shapes.

my $shape = new shapeObj(-1);

Grab shape #$record and stick it into the shape holder.

$shapefile->get($record - 1, $shape);

Add that shape to the selection set shapefile.

$shapesel->add($shape);
}

Close the new shapefile.

undef $shapesel;

Create dbf to go with it.

my $newdbh = $dbh->create("name" => "selected.dbf");

Reopen the selected set shapefile.

$shapesel = new shapefileObj("selected", -1);

Get the extent of selected set.

$newrect = $shapesel->{bounds};
$newminx = $newrect->{minx};
$newmaxx = $newrect->{maxx};
$newminy = $newrect->{miny};
$newmaxy = $newrect->{maxy};
$numseld = $shapesel->{numshapes};
undef $shapesel;

Print the extents.

print "The Extents of the Selected Set: minx=$newminx miny=$newminy maxx=$newmaxx maxy=$newmaxy.\n";

Print the number of selected records.

print "The Number of Selected Shapes = $numseld.\n";
}}}

back to PerlMapScrip

Clone this wiki locally