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         61  
4 2     2   19 use warnings;
  2         9  
  2         72  
5 2     2   1171 use POSIX qw /floor/;
  2         12800  
  2         10  
6 2     2   2920 use Carp;
  2         5  
  2         100  
7 2     2   1064 use autovivification;
  2         1576  
  2         8  
8            
9             our $VERSION = '3.01';
10            
11             # should also handle X cells
12             sub new {
13 12     12 1 51 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         31 $n = int $n;
19 12 50       66 die 'Number of blocks must be positive and >=1'
20             if $n <= 0;
21            
22 12         49 my $y_range = abs ($y_max - $y_min);
23 12         41 my $y_tol = $y_range / 1000;
24 12         56 $y_range += 2 * $y_tol;
25 12         33 $y_min -= $y_tol;
26 12         25 $y_max += $y_tol;
27            
28 12         31 my $block_ht = $y_range / $n;
29            
30 12         48 $self->{x_min} = $x_min;
31 12         35 $self->{y_min} = $y_min;
32 12         38 $self->{x_max} = $x_max;
33 12         60 $self->{y_max} = $y_max;
34 12         32 $self->{y_res} = $block_ht;
35 12         38 $self->{y_n} = $n;
36 12         35 $self->{x_n} = 1;
37            
38 12         31 my %blocks;
39 12         37 my $y = $y_min;
40 12         49 foreach my $i (1 .. $n) {
41 175         321 my $key = $self->snap_to_index($x_min, $y); # index by lower left
42 175         465 $blocks{$key} = [];
43 175         287 $y += $block_ht;
44             }
45 12         53 $self->{containers} = \%blocks;
46            
47 12         46 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 27593 sub get_y_min {$_[0]->{y_min}}
53 0     0 1 0 sub get_y_max {$_[0]->{y_max}}
54 14834     14834 1 25024 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   326603 my ($self, $id) = @_;
59            
60 2     2   900 no autovivification;
  2         4  
  2         8  
61            
62 221226         306749 my $containers = $self->{containers};
63 221226   100     441498 my $container = $containers->{$id} || [];
64            
65 221226         349176 return $container;
66             };
67            
68             # need to handle X coords as well
69             sub snap_to_index {
70 14834     14834 1 23986 my ($self, $x, $y) = @_;
71            
72             #my $x_min = $self->get_x_min;
73 14834         24253 my $y_min = $self->get_y_min;
74 14834         24617 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         24294 my $partial = ($y - $y_min) / $y_res;
79 14834         29837 my $y_block = floor ($partial * 1.001);
80            
81 14834 100       38576 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 14596 my ($self, $item, @bbox) = @_;
87            
88 7289         14402 my @index_id1 = $self->snap_to_index (@bbox[0, 1]);
89 7289         14009 my @index_id2 = $self->snap_to_index (@bbox[2, 3]);
90            
91 7289         10417 my $insert_count = 0;
92 7289         15935 foreach my $y ($index_id1[1] .. $index_id2[1]) {
93 221145         327483 my $index_id = "0:$y"; # hackish
94 221145         339901 my $container = $self->_get_container_ref ($index_id);
95 221145         330939 push @$container, $item;
96 221145         319582 $insert_count++;
97             }
98            
99 7289         22334 return $insert_count;
100             }
101            
102             # $storage ref arg is for Tree::R compat - still needed?
103             sub query_point {
104 81     81 1 189 my ($self, $x, $y, $storage_ref) = @_;
105            
106 81         237 my $index_id = $self->snap_to_index ($x, $y);
107 81         240 my $container = $self->_get_container_ref ($index_id);
108            
109 81 50       217 if ($storage_ref) {
110 0         0 push @$storage_ref, @$container;
111             }
112            
113 81 50       6001 return wantarray ? @$container : [@$container];
114             }
115            
116            
117             1;
118            
119             __END__