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   14 use strict;
  2         4  
  2         58  
4 2     2   13 use warnings;
  2         20  
  2         56  
5 2     2   1156 use POSIX qw /floor/;
  2         12645  
  2         11  
6 2     2   2894 use Carp;
  2         5  
  2         96  
7 2     2   1013 use autovivification;
  2         1562  
  2         10  
8            
9             our $VERSION = '3.00';
10            
11             # should also handle X cells
12             sub new {
13 12     12 1 47 my ($class, $n, $x_min, $y_min, $x_max, $y_max) = @_;
14            
15 12         37 my $self = bless {}, $class;
16            
17 12   50     45 $n ||= 10; # need a better default?
18 12         26 $n = int $n;
19 12 50       41 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         34 my $y_tol = $y_range / 1000;
24 12         32 $y_range += 2 * $y_tol;
25 12         27 $y_min -= $y_tol;
26 12         26 $y_max += $y_tol;
27            
28 12         29 my $block_ht = $y_range / $n;
29            
30 12         39 $self->{x_min} = $x_min;
31 12         30 $self->{y_min} = $y_min;
32 12         32 $self->{x_max} = $x_max;
33 12         34 $self->{y_max} = $y_max;
34 12         36 $self->{y_res} = $block_ht;
35 12         29 $self->{y_n} = $n;
36 12         31 $self->{x_n} = 1;
37            
38 12         24 my %blocks;
39 12         27 my $y = $y_min;
40 12         37 foreach my $i (1 .. $n) {
41 175         316 my $key = $self->snap_to_index($x_min, $y); # index by lower left
42 175         522 $blocks{$key} = [];
43 175         320 $y += $block_ht;
44             }
45 12         59 $self->{containers} = \%blocks;
46            
47 12         54 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 29682 sub get_y_min {$_[0]->{y_min}}
53 0     0 1 0 sub get_y_max {$_[0]->{y_max}}
54 14834     14834 1 25571 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   353160 my ($self, $id) = @_;
59            
60 2     2   826 no autovivification;
  2         4  
  2         10  
61            
62 221226         347044 my $containers = $self->{containers};
63 221226   100     477029 my $container = $containers->{$id} || [];
64            
65 221226         396589 return $container;
66             };
67            
68             # need to handle X coords as well
69             sub snap_to_index {
70 14834     14834 1 25575 my ($self, $x, $y) = @_;
71            
72             #my $x_min = $self->get_x_min;
73 14834         25585 my $y_min = $self->get_y_min;
74 14834         26293 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         25688 my $partial = ($y - $y_min) / $y_res;
79 14834         30609 my $y_block = floor ($partial * 1.001);
80            
81 14834 100       39557 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 15567 my ($self, $item, @bbox) = @_;
87            
88 7289         14968 my @index_id1 = $self->snap_to_index (@bbox[0, 1]);
89 7289         14119 my @index_id2 = $self->snap_to_index (@bbox[2, 3]);
90            
91 7289         12393 my $insert_count = 0;
92 7289         16535 foreach my $y ($index_id1[1] .. $index_id2[1]) {
93 221145         351591 my $index_id = "0:$y"; # hackish
94 221145         367070 my $container = $self->_get_container_ref ($index_id);
95 221145         354946 push @$container, $item;
96 221145         355406 $insert_count++;
97             }
98            
99 7289         23562 return $insert_count;
100             }
101            
102             # $storage ref arg is for Tree::R compat - still needed?
103             sub query_point {
104 81     81 1 195 my ($self, $x, $y, $storage_ref) = @_;
105            
106 81         183 my $index_id = $self->snap_to_index ($x, $y);
107 81         222 my $container = $self->_get_container_ref ($index_id);
108            
109 81 50       204 if ($storage_ref) {
110 0         0 push @$storage_ref, @$container;
111             }
112            
113 81 50       5386 return wantarray ? @$container : [@$container];
114             }
115            
116            
117             1;
118            
119             __END__