File Coverage

lib/Geo/ShapeFile/Shape/Index.pm
Criterion Covered Total %
statement 71 75 94.6
branch 5 8 62.5
condition 3 4 75.0
subroutine 13 16 81.2
pod 9 9 100.0
total 101 112 90.1


line stmt bran cond sub pod time code
1             package Geo::ShapeFile::Shape::Index;
2             #use 5.010; # not yet
3 2     2   15 use strict;
  2         5  
  2         59  
4 2     2   11 use warnings;
  2         10  
  2         64  
5 2     2   1137 use POSIX qw /floor/;
  2         13070  
  2         10  
6 2     2   2916 use Carp;
  2         4  
  2         133  
7 2     2   936 use autovivification;
  2         1670  
  2         9  
8              
9             our $VERSION = '3.03';
10              
11             # should also handle X cells
12             sub new {
13 12     12 1 46 my ($class, $n, $x_min, $y_min, $x_max, $y_max) = @_;
14              
15 12         39 my $self = bless {}, $class;
16              
17 12   50     52 $n ||= 10; # need a better default?
18 12         32 $n = int $n;
19 12 50       40 die 'Number of blocks must be positive and >=1'
20             if $n <= 0;
21              
22 12         44 my $y_range = abs ($y_max - $y_min);
23 12         35 my $y_tol = $y_range / 1000;
24 12         30 $y_range += 2 * $y_tol;
25 12         23 $y_min -= $y_tol;
26 12         20 $y_max += $y_tol;
27              
28 12         34 my $block_ht = $y_range / $n;
29              
30 12         35 $self->{x_min} = $x_min;
31 12         36 $self->{y_min} = $y_min;
32 12         24 $self->{x_max} = $x_max;
33 12         35 $self->{y_max} = $y_max;
34 12         23 $self->{y_res} = $block_ht;
35 12         32 $self->{y_n} = $n;
36 12         28 $self->{x_n} = 1;
37              
38 12         24 my %blocks;
39 12         19 my $y = $y_min;
40 12         36 foreach my $i (1 .. $n) {
41 175         317 my $key = $self->snap_to_index($x_min, $y); # index by lower left
42 175         661 $blocks{$key} = [];
43 175         288 $y += $block_ht;
44             }
45 12         51 $self->{containers} = \%blocks;
46              
47 12         43 return $self;
48             }
49              
50 0     0 1 0 sub get_x_min {$_[0]->{x_min}}
51 0     0 1 0 sub get_x_max {$_[0]->{x_max}}
52 14834     14834 1 29509 sub get_y_min {$_[0]->{y_min}}
53 0     0 1 0 sub get_y_max {$_[0]->{y_max}}
54 14834     14834 1 22752 sub get_y_res {$_[0]->{y_res}}
55              
56             # return an anonymous array if we are out of the index bounds
57             sub _get_container_ref {
58 221226     221226   329656 my ($self, $id) = @_;
59              
60 2     2   794 no autovivification;
  2         25  
  2         11  
61              
62 221226         312439 my $containers = $self->{containers};
63 221226   100     442747 my $container = $containers->{$id} || [];
64              
65 221226         401417 return $container;
66             };
67              
68             # need to handle X coords as well
69             sub snap_to_index {
70 14834     14834 1 24644 my ($self, $x, $y) = @_;
71              
72             #my $x_min = $self->get_x_min;
73 14834         24855 my $y_min = $self->get_y_min;
74 14834         24147 my $y_res = $self->get_y_res;
75              
76             # take the floor, but add a small tolerance to
77             # avoid precision issues with snapping
78 14834         23328 my $partial = ($y - $y_min) / $y_res;
79 14834         27055 my $y_block = floor ($partial * 1.001);
80              
81 14834 100       40976 return wantarray ? (0, $y_block) : "0:$y_block";
82             }
83              
84             # inserts into whichever blocks overlap the bounding box
85             sub insert {
86 7289     7289 1 15072 my ($self, $item, @bbox) = @_;
87              
88 7289         14714 my @index_id1 = $self->snap_to_index (@bbox[0, 1]);
89 7289         14381 my @index_id2 = $self->snap_to_index (@bbox[2, 3]);
90              
91 7289         10407 my $insert_count = 0;
92 7289         17375 foreach my $y ($index_id1[1] .. $index_id2[1]) {
93 221145         330120 my $index_id = "0:$y"; # hackish
94 221145         333972 my $container = $self->_get_container_ref ($index_id);
95 221145         332697 push @$container, $item;
96 221145         317894 $insert_count++;
97             }
98              
99 7289         23911 return $insert_count;
100             }
101              
102             # $storage ref arg is for Tree::R compat - still needed?
103             sub query_point {
104 81     81 1 172 my ($self, $x, $y, $storage_ref) = @_;
105              
106 81         164 my $index_id = $self->snap_to_index ($x, $y);
107 81         194 my $container = $self->_get_container_ref ($index_id);
108              
109 81 50       187 if ($storage_ref) {
110 0         0 push @$storage_ref, @$container;
111             }
112              
113 81 50       4731 return wantarray ? @$container : [@$container];
114             }
115              
116              
117             1;
118              
119             __END__