File Coverage

blib/lib/Geo/Walkabout/Chain.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             # $Id: Chain.pm,v 1.1.1.1 2000/12/05 00:55:01 schwern Exp $
2              
3             # Somebody's going to want to graft this thing onto another database.
4             # May as well plan for it.
5             package Geo::Walkabout::Chain;
6             @ISA = qw(Geo::Walkabout::Chain::PostgreSQL);
7              
8              
9             package Geo::Walkabout::Chain::PostgreSQL;
10              
11 2     2   840 use strict;
  2         3  
  2         71  
12 2     2   9302 use AnyLoader;
  0            
  0            
13             use Carp::Assert;
14              
15             use vars qw($VERSION);
16              
17             $VERSION = '0.02';
18              
19              
20             =pod
21              
22             =head1 NAME
23              
24             Geo::Walkabout::Chain - An open path representing the shape of a line feature.
25              
26             =head1 SYNOPSIS
27              
28             require Geo::Walkabout::Chain;
29              
30             my $chain = Geo::Walkabout::Chain->new([1,1],[5,10.2],[12,13]);
31             my $chain = Geo::Walkabout::Chain->new_from_pgpath('[(1,1),(5,10.2),(12,13)]');
32              
33             my $begin = $chain->begin;
34             my $end = $chain->end;
35             my @shape = $chain->shape;
36             my @raw_chain = $chain->chain;
37             my $pg_path = $chain->as_pgpath;
38              
39             $chain->append_shape(@points);
40              
41              
42             =head1 DESCRIPTION
43              
44             This is a representation of a complete chain. Typically, it should
45             not be used directly, instead Geo::Walkabout::Line encapsulates a
46             single Geo::Walkabout::Chain.
47              
48             A single point in a chain is represented as a two element array
49             representing a single point of latitude and longitutde. (OO dogma
50             says these should be objects, too, but if I wanted to be that silly
51             I'd be using Java.)
52              
53             =head1 Public Methods
54              
55             =head2 Constructors
56              
57             =over 4
58              
59             =item B
60              
61             my $chain = Geo::Walkabout::Chain->new([$lat1, $long1],
62             [$lat2, $long2],
63             ...
64             );
65              
66             Creates a new Geo::Walkabout::Chain object from a list of points (two
67             element array references). The first point is the start of the chain,
68             the last is the end (or vice-versa depending on which way you look.)
69             The rest are "shape" coordinates.
70              
71              
72             =cut
73              
74             #'#
75             sub new {
76             my($class, @chain) = @_;
77              
78             unless( @chain >= 2 ) {
79             Carp::carp("A chain must have at least a start and an end.");
80             return;
81             }
82            
83             my($self) = [@chain];
84             return bless $self, $class;
85             }
86              
87             =pod
88              
89             =item B
90              
91             my $chain = Geo::Walkabout::Chain->new_from_pgpath($postgres_path);
92              
93             An alternative constructor, it takes a PostgreSQL style open PATH of the
94             form:
95              
96             [ ( lat1, long1 ), ... , (latn, longn) ]
97              
98             So something like '[(1,1), (-1,2.2), (-2,3)]'. This is very helpful when
99             reading in chains from a PostgreSQL database.
100              
101             =cut
102              
103             sub new_from_pgpath {
104             my($class, $chain) = @_;
105              
106             my($self) = $class->new( $class->_split_pg_path($chain) );
107             return $self;
108             }
109              
110             =pod
111              
112             =back
113              
114              
115             =head2 Accessors
116              
117             =over 4
118              
119             =item B
120              
121             my $beginning_point = $chain->begin;
122              
123             Returns the beginning point of this chain as a two element array reference.
124              
125             =item B
126              
127             my $end_point = $chain->end;
128              
129             Returns the end point of this chain as a two element array reference.
130              
131             =cut
132              
133             sub begin {
134             return $_[0]->[0];
135             }
136              
137             sub end {
138             return $_[0]->[-1];
139             }
140              
141             =pod
142              
143             =item B
144              
145             my @shape = $chain->shape;
146              
147             Returns the shaping points of this chain, ie. those points between the
148             start and the end which determine the shape of the chain (without
149             them, its just a line segment).
150              
151             =cut
152              
153             sub shape {
154             my $self = shift;
155             return @{$self}[1..$#{$self} - 1];
156             }
157              
158             =pod
159              
160             =item B
161              
162             my @raw_chain = $chain->chain;
163              
164             Dumps the chain this object represents as a series of points. This is
165             equivalent to:
166              
167             my @raw_chain = ($chain->begin, $chain->shape, $chain->end);
168              
169             =cut
170              
171             sub chain {
172             return @{$_[0]};
173             }
174              
175             =pod
176              
177             =item B
178              
179             my $pg_path = $chain->as_pgpath;
180              
181             Returns a representation of the chain as a PostgreSQL open path
182             suitable for insertion into the database.
183              
184             =cut
185              
186             sub as_pgpath {
187             my($self) = shift;
188              
189             return '['. join(', ', map { "(". join(',', @$_) .")" } @$self) .']';
190             }
191              
192             =pod
193              
194             =item B
195              
196             my $pg_point = $chain->to_pgpoint(\@point);
197              
198             Translates a two element array reference into a PostgreSQL point.
199              
200             =cut
201              
202             sub to_pgpoint {
203             my($self, $point) = @_;
204              
205             assert(@$point == 2);
206              
207             return '('. join(', ', @$point) .')';
208             }
209              
210             =pod
211              
212             =back
213              
214              
215             =head2 Modifiers
216              
217             =over 4
218              
219             =item B
220              
221             $chain->append_shape(@points);
222              
223             Adds new shaping points to the chain. They are appended to the end of
224             the shape.
225              
226             =cut
227              
228             sub append_shape {
229             my($self, @points) = @_;
230              
231             splice @$self, -1, 0, @points;
232             }
233              
234             =pod
235              
236             =head2 Private Methods
237              
238             B I document them here because I'm forgetful. Use of these
239             may result in I (consequences may contain one or
240             more of the following: pain, death, dismemberment, yellow dye #5)
241              
242              
243             =over 4
244              
245             =item B<_split_pg_path>
246              
247             my @path = Geo::Walkabout::Chain->_split_pg_path($pg_path);
248              
249             Converts a PostgreSQL open PATH into an array of points.
250              
251             =cut
252              
253             #'#
254             sub _split_pg_path {
255             my($self, $path) = @_;
256            
257             # A bit of sanity checking.
258             unless( $path =~ /^\s*\[.*\]\s*$/ ) {
259             Carp::carp('This doesn\'t look like a PostgreSQL open PATH');
260             return;
261             }
262              
263             my @points = ();
264             # ( 4.4 , -6.9 )
265             while( $path =~ / \( \s* ([-\d\.]+) \s* ,
266             \s* ([-\d\.]+) \s* \)
267             /gx )
268             {
269             push @points, [$1,$2];
270             }
271              
272             return @points;
273             }
274              
275             =pod
276              
277             =head1 AUTHOR
278              
279             Michael G Schwern
280              
281              
282             =head1 SEE ALSO
283              
284             B, B, B
285              
286             =cut
287              
288             1;