Geo::Shapelib(3pm)

πŸ’– Geo::Shapelib(3pm) πŸ’–

User Contributed Perl Documentation, 3pm section, bestie. okay so this page is literally the whole vibe for Geo::Shapelib. πŸ’…

🌸 NAME 🌸

Geo::Shapelib β€” a Perl extension for reading and writing shapefiles as defined by ESRI(r). No cap, it's giving geographic data realness. πŸ—ΊοΈβœ¨

✨ SYNOPSIS ✨

First up, the basic import:

    use Geo::Shapelib qw/:all/;

or

    use Geo::Shapelib qw/:all/;

    my $shapefile = new Geo::Shapelib {
        Name => 'stations',
        Shapetype => POINT,
        FieldNames => ['Name','Code','Founded'],
        FieldTypes => ['String:50','String:10','Integer:8']
    };

    while (<DATA>) {
        chomp;
        my($station,$code,$founded,$x,$y) = split /\|/;
        push @{$shapefile->{Shapes}},{ Vertices => [[$x,$y,0,0]] };
        push @{$shapefile->{ShapeRecords}}, [$station,$code,$founded];
    }

    $shapefile->save();

πŸ’– DESCRIPTION πŸ’–

okay so this is the library for reading, creating, and writing shapefiles as defined by ESRI(r) using Perl. The Perl code uses Frank Warmerdam's Shapefile C Library (http://shapelib.maptools.org/). And bestie, the library is included in this distribution. Slay. 🫢

Currently no methods exist for populating an empty Shape. You need to do it in your own code. This is how:

First you include the module into your code. If you want to define the shape type using its name, import all:

    use Geo::Shapelib qw/:all/;

Create the shapefile object and specify its name and type:

    $shapefile = new Geo::Shapelib {
        Name => <filename>,
        Shapetype => <type from the list>,
        FieldNames => <field name list>,
        FieldTypes => <field type list>
    }

The name (filename, may include path) of the shapefile, the extension is not used (it is stripped in the save method).

The shape type is an integer. This module defines shape type names as constants (see below).

The field name list is an array reference of the names of the data items assigned to each shape.

The field type list is an array reference of the types of the data items. Field type is either 'Integer', 'Double', or 'String'.

The types may have optional 'width' and 'decimals' fields defined, like this:

'Integer[:width]' defaults: width = 10
'Double[:width[:decimals]]' defaults: width = 10, decimals = 4
'String[:width]' defaults: width = 255

There are some other attributes which can be defined in the constructor (see below), they are rarely needed. The shape object will need or get a couple of other attributes as well. They should be treated as private:

  • $shapefile->{NShapes} is the number of shapes in your object. Shapefile is a collection of shapes. This is usually automatically deduced from the Shapes array when needed.
  • $shapefile->{MinBounds} is set by shapelib C functions.
  • $shapefile->{MaxBounds} is set by shapelib C functions.

Create the shapes and respective shape records and put them into the shape:

    for many times {
        make $s, a new shape as a reference to a hash
        push @{$shapefile->{Shapes}}, $s;
        make $r, a shape record as a reference to an array
        push @{$shapefile->{ShapeRecords}}, $r;
    }

how to create $s? It is a (reference to an) hash.

set:

$s->{Vertices} this is a reference to an array of arrays of four values, one for each vertex: x, y, z, and m of the vertex. There should be at least one vertex in $s. Point has only one vertex.

$s->{Parts}:

$s->{Parts} is not needed in simple cases. $s->{Parts} is a reference to an array (a) of arrays (b). There is one (b) array for each part. In a (b) array the first value is an index to the Vertices array denoting the first vertex of that part. The second value is the type of the part (NOTE: not the type of the shape). The type is 5 (Ring) unless the shape is of type Multipatch. The third value is set as the type of the part as a string when reading from a file but the save method requires only the first two values.

The index of the last vertex of any part is implicitly the index of the next part minus one or the index of the last vertex.

forget these:

  • $s->{ShapeId} may be left undefined. The save method sets it to the index in the Shapes array. Instead create and use an id field in the record.
  • $s->{NParts} and $s->{NVertices} may be set but that is usually not necessary since they are calculated in the save method. You only need to set these if you want to save less parts or vertices than there actually are in the Parts or Vertices arrays.
  • $s->{SHPType} is the type of the shape and it is automatically set to $shape->{Shapetype} unless defined (which you should not do)

The shape record is simply an array reference, for example:

    $r = [item1,item2,item3,...];

That's all. Then save it and start your shapefile viewer to look at the result. πŸ’–

πŸŽ€ EXPORT πŸŽ€

None by default. The following export tags are defined.

  • :constants β€” This exports constant functions for the individual types of shapefile Types and shapefile part types. They all return scalar (integer) values. The shapetype functions: POINT, ARC, POLYGON, MULTIPOINT, POINTZ, ARCZ, POLYGONZ, MULTIPOINTZ, POINTM, ARCM, POLYGONM, MULTIPOINTM, MULTIPATCH are defined. The shapefile part types: TRISTRIP, TRIFAN, OUTERRING, INNERRING, FIRSTRING, RING are defined.
  • :types β€” Exports two hashes: %ShapeTypes, %PartTypes which map the shapelib type integers to string values.
  • :all β€” All possible exports are included.

πŸ¦‹ CONSTRUCTORS πŸ¦‹

This one reads in an existing shapefile:

    $shapefile = new Geo::Shapelib "myshapefile", {<options>};

This one creates a new, blank Perl shapefile object:

    $shapefile = new Geo::Shapelib {<options>};

{<options>} is optional in both cases, an example (note the curly braces):

   $shapefile = new Geo::Shapelib {
       Name => $shapefile,
       Shapetype => POINT,
       FieldNames => ['Name','Code','Founded'],
       FieldTypes => ['String:50','String:10','Integer:8']
   };

   $shapefile = new Geo::Shapelib "myshapefile" {
       Rtree => 1
   };

πŸ’… Options:

  • Like β€” A shapefile from which to copy ShapeType, FieldNames, and FieldTypes.
  • Name β€” Default is "shapefile". The filename (if given) becomes the name for the shapefile unless overridden by this.
  • Shapetype β€” Default "POINT". The type of the shapes. (All non-null shapes in a shapefile are required to be of the same shape type.)
  • FieldNames β€” Default is [].
  • FieldTypes β€” Default is [].
  • ForceStrings β€” Default is 0. If 1, sets all FieldTypes to string, may be useful if values are very large ints.
  • Rtree β€” Default is 0. If 1, creates an R-tree of the shapes into an element Rtree. (Requires LoadAll.)

When a shapefile is read from files they end up in a bit different kind of data structure than what is expected by the save method for example and what is described above. These flags enable the conversion, they are not normally needed.

  • CombineVertices β€” Default is 1. CombineVertices is experimental. The default behavior is to put all vertices into the Vertices array and part indexes into the Parts array. If CombineVertices is set to 0 there is no Vertices array and all data goes into the Parts. Currently setting CombineVertices to 0 breaks saving of shapefiles. πŸ’€
  • UnhashFields β€” Default is 1. Makes $self's attributes FieldNames, FieldTypes refs to lists, and ShapeRecords a list of lists.

The default is to load all data into Perl variables in the constructor. With these options the data can be left into the files to be loaded on-demand.

  • Load β€” Default is 1. If 0, has the same effect as LoadRecords=>0 and LoadAll=>0.
  • LoadRecords β€” Default is 1. Reads shape records into $self->{ShapeRecords} automatically in the constructor using the get_record($shape_index) method.
  • LoadAll β€” Default is 1. Reads shapes (the geometry data) into $self->{Shapes} automatically in the constructor using the get_shape($shape_index) method.

data_model 🧚

Returns the data model, converted into two arrays. That's it, that's the job.

If a filename got handed to a constructor at some point, then the data model was read straight out of the dbf file and stashed as a hashref in the FieldTypes attribute. This method takes that hashref and splits it into two arrays: FieldNames and the matching FieldTypes. Those arrayrefs get stored in attributes with those exact names β€” but only if UnhashFields is TRUE. πŸ’–

get_shape(shape_index, from_file) πŸ¦‹

Returns shape nr. shape_index+1 (heads up bestie, the first index is 0, not 1). The shape is read from a file even if the array Shapes exists, whenever from_file is TRUE.

Option CombineVertices is in operation here, fyi.

Use this method to get a shape unless you know what you are doing. πŸ‘‘

get_record(shape_index, from_file) 🌸

Returns the record that belongs to shape nr. shape_index+1 (again, first index is 0). The record is read from a file even if the array ShapeRecords exists, whenever from_file is TRUE.

get_record_arrayref(shape_index, FieldNames, from_file) πŸŽ€

Returns the record belonging to shape nr. shape_index+1 (first index 0, you know the drill) as an arrayref. The parameter FieldNames may be undef, but if it is defined, it's used as the array that the record array gets sorted according to. This matters in the case where ShapeRecords contains hashrefs. The record is read from the file even if the array ShapeRecords exists, whenever from_file is TRUE.

Use this method to get a record of a shape unless you know what you are doing. πŸ’…

get_record_hashref(shape_index, from_file) πŸ’œ

Returns the record belonging to shape nr. shape_index+1 (first index 0) as a hashref. The record is read from the file even if the array ShapeRecords exists, whenever from_file is TRUE. And if records are sitting in the array ShapeRecords as a list of lists, then FieldNames must contain the names of the fields. No skips, no shortcuts.

Use this method to get a record of a shape unless you know what you are doing. πŸ™ˆ

lengths(shape) ⭐

Returns the lengths of the parts of the shape. That means the lengths of the parts of a polyline, or the length of the boundary of a polygon. Both 2D and 3D data are taken into account. She's thorough like that. πŸ”₯

🌈 Using shapefile quadtree spatial indexing 🌈

Want a list of shape ids within the specified bound, using a shapefile quadtree index? Say less:

$shapefile->query_within_rect($bounds, $maxdepth = 0);

$bounds should be an array reference of 4 elements: (xmin, ymin, xmax, ymax).

This method uses the quadtree indices defined by Shapelib β€” not ESRI spatial index files (.sbn, .sbx). And if a quadtree index (<basename>.qix) doesn't exist yet, one gets created and saved as a file. Not me creating files without asking, but okay bestie, slay. ✨

If you just want to create an index, there's a method for that too:

$shapefile->create_spatial_index($maxdepth = 0);

$maxdepth (optional) is the maximum depth of the index to create. Default is 0, which means shapelib will calculate a reasonable default depth for you. She's got it. πŸ’–

🌳 Rtree and editing the shapefile 🌳

Building an R-tree for the shapes:

$shapefile->Rtree();

This happens automatically if the Rtree-option is set when a shapefile is loaded from files.

Then you can use methods like these (there are not yet any wrappers for them, so get cozy with the raw calls):

my @shapes;
$shapefile->{Rtree}->query_point(@xy,\@shapes); # or
$shapefile->{Rtree}->query_completely_within_rect(@rect,\@shapes); # or
$shapefile->{Rtree}->query_partly_within_rect(@rect,\@shapes);

That gives you a list of shapes (indexes into the shape array), which you can feed for example to the select_vertices function:

for my $shape (@shapes) {
    my $vertices = $shapefile->select_vertices($shape,@rect);
    my $n = @$vertices;
    print "you selected $n vertices from shape $shape\n";
}

The shapefile object remembers the selected vertices (iconic behaviour, honestly), and calling:

$shapefile->move_selected_vertices($dx,$dy);

moves them. The bboxes of the affected shapes, and the R-tree if one exists, are updated automatically. And to clear all selections from all shapes, call:

$selected->clear_selections();

πŸ“ Setting the bounds of the shapefile πŸ“

$shapefile->set_bounds;

Sets the MinBounds and MaxBounds of all shapes and of the shapefile. Everyone gets bounds. No one left out. 🫢

πŸ’Ύ Saving the shapefile πŸ’Ύ

$shapefile->save($filename);

The argument $shapefile is optional β€” the internal attribute $shapefile->{Name} gets used if $filename isn't specified. And if $filename is specified, it also becomes the new name.

$filename may contain an extension, but it gets removed and .shp etc. are used instead. Sorry to that extension, it's not staying. πŸ’€

If you are not sure that the bounds of the shapefile are ok, then call $shapefile->set_bounds; before saving. Do it. Future you says thank you.

πŸ› οΈ create, add, close πŸ› οΈ

$shapefile->create($filename);

and then, many times over:

$shapefile->add($shape, $record);

and finally:

$shapefile->close();

These methods make it easy to create large shapefiles. $filename is optional. Heads up though: these methods create some temporary variables (prefix: _) in internal data, and that's exactly why calling the close method is required. Don't ghost the close. 😭

πŸ—‘οΈ Dump πŸ—‘οΈ

$shapefile->dump($to);

$to can be undef (in which case dump uses STDOUT), a filename, or a reference to a filehandle (e.g., \*DUMP).

This method just dumps all the data. And a lil warning: if you have created the shapefile yourself, then the reported bounds may be incorrect. πŸ™ˆ


πŸ‘‘ AUTHOR πŸ‘‘

Ari Jolma, https://github.com/ajolma


🏠 REPOSITORY 🏠

https://github.com/ajolma/Geo-Shapelib


That's the whole page bestie, you made it to the end πŸ’– This little gem comes from perl v5.40.1, dated 2025-03-06, and it lives under the name Geo::Shapelib(3pm). Go read the repo, go build something iconic with your shapefiles, and remember: call close. Periodt. πŸ’…βœ¨

πŸ“¦ where this page comes from