File Coverage

lib/Geo/WKT.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             # Copyrights 2008-2012 by [Mark Overmeer].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.00.
5 7     7   184552 use strict;
  7         16  
  7         265  
6 7     7   35 use warnings;
  7         11  
  7         279  
7              
8             package Geo::WKT;
9 7     7   44 use vars '$VERSION';
  7         13  
  7         502  
10             $VERSION = '0.04';
11              
12 7     7   55 use base 'Exporter';
  7         13  
  7         822  
13              
14 7     7   10880 use Geo::Shape ();
  0            
  0            
15             use Carp;
16              
17             our @EXPORT = qw(
18             parse_wkt
19             parse_wkt_point
20             parse_wkt_polygon
21             parse_wkt_geomcol
22             parse_wkt_linestring
23             wkt_point
24             wkt_multipoint
25             wkt_linestring
26             wkt_polygon
27             wkt_linestring
28             wkt_multilinestring
29             wkt_multipolygon
30             wkt_optimal
31             wkt_geomcollection
32             );
33              
34             sub wkt_optimal($);
35              
36              
37             sub parse_wkt_point($;$)
38             { ($_[0] =~ m/^point\(\s*(\S+)\s+(\S+)\)$/i)
39             ? Geo::Point->xy($1+0, $2+0, $_[1])
40             : undef;
41             }
42              
43              
44             sub parse_wkt_polygon($;$)
45             { my ($string, $proj) = @_;
46              
47             $string && $string =~ m/^polygon\(\((.+)\)\)$/i
48             or return undef;
49              
50             my @poly;
51             foreach my $poly (split m/\)\s*\,\s*\(/, $1)
52             { my @points = map { [split " ", $_, 2] } split /\s*\,\s*/, $poly;
53             push @poly, \@points;
54             }
55              
56             Geo::Surface->new(@poly, proj => $proj);
57             }
58              
59              
60             sub parse_wkt_geomcol($;$)
61             { my ($string, $proj) = @_;
62              
63             return undef
64             if $string !~
65             s/^(multiline|multipoint|multipolygon|geometrycollection)\(//i;
66              
67             my @comp;
68             while($string =~ m/\D/)
69             { last unless $string =~ s/^[^(]*\([^)]*\)//;
70             my $take = $&;
71             while(1)
72             { my @open = $take =~ m/\(/g;
73             my @close = $take =~ m/\)/g;
74             last if @open==@close;
75             $take .= $& if $string =~ s/^[^\)]*\)//;
76             }
77             push @comp, parse_wkt($take, $proj);
78             $string =~ s/^\s*\,\s*//;
79             }
80              
81             Geo::Space->new
82             ( @comp
83             , proj => $proj
84             );
85             }
86              
87              
88             sub parse_wkt_linestring($;$)
89             { my ($string, $proj) = @_;
90              
91             $string && $string =~ m/^linestring\((.+)\)$/i
92             or return undef;
93              
94             my @points = map { [split " ", $_, 2] } split /\s*\,\s*/, $1;
95             @points > 1 or return;
96              
97             Geo::Line->new(proj => $proj, points => \@points, filled => 0);
98             }
99              
100              
101             sub parse_wkt($;$) # dirty code to avoid copying the sometimes huge string
102             {
103             $_[0] =~ m/^point\(/i ? &parse_wkt_point
104             : $_[0] =~ m/^polygon\(/i ? &parse_wkt_polygon
105             : $_[0] =~ m/^linestring\(/i ? &parse_wkt_polygon
106             : &parse_wkt_geomcol;
107             }
108              
109              
110             sub _list_of_points(@)
111             { my @points
112             = @_ > 1 ? @_
113             : ref $_[0] eq 'ARRAY' ? @{$_[0]}
114             : $_[0]->isa('Math::Polygon') ? $_[0]->points
115             : $_[0];
116              
117             my @s = map
118             { (ref $_ ne 'ARRAY' && $_->isa('Geo::Point'))
119             ? $_->x.' '.$_->y
120             : $_->[0].' '.$_->[1]
121             } @points;
122              
123             local $" = ',';
124             "(@s)";
125             }
126              
127             sub wkt_point($;$)
128             { my ($x, $y)
129             = @_==2 ? @_
130             : ref $_[0] eq 'ARRAY' ? @{$_[0]}
131             : shift->xy;
132              
133             defined $x && defined $y ? "POINT($x $y)" : ();
134             }
135              
136              
137             sub wkt_linestring(@) { 'LINESTRING' . _list_of_points(@_) }
138              
139              
140             sub wkt_polygon(@)
141             { my @polys
142             = !defined $_[0] ? return ()
143             : ref $_[0] eq 'ARRAY' ? (ref $_[0][0] ? @_ : [@_])
144             : $_[0]->isa('Geo::Line') ? @_
145             : $_[0]->isa('Geo::Surface') ? ($_[0]->outer, $_[0]->inner)
146             : [@_];
147              
148             'POLYGON('
149             . join( ',' , map { _list_of_points $_ } @polys)
150             . ')';
151             }
152              
153              
154             sub wkt_multipoint(@) { 'MULTIPOINT('. join(',', map {wkt_point($_)} @_) .')'}
155              
156              
157             sub wkt_multilinestring(@)
158             { return () unless @_;
159              
160             'MULTILINESTRING('
161             . join( ',' , map { wkt_linestring $_ } @_)
162             . ')';
163             }
164              
165              
166             sub wkt_multipolygon(@)
167             { return () unless @_;
168              
169             my @polys = map { wkt_polygon $_ } @_;
170             s/^POLYGON// for @polys;
171              
172             'MULTIPOLYGON('.join( ',' , @polys). ')';
173             }
174              
175              
176              
177             sub wkt_optimal($)
178             { my $geom = shift;
179             return wkt_point(undef) unless defined $geom;
180              
181             return wkt_point($geom)
182             if $geom->isa('Geo::Point');
183              
184             return ( $geom->isRing && $geom->isFilled
185             ? wkt_polygon($geom)
186             : wkt_linestring($geom))
187             if $geom->isa('Geo::Line');
188              
189             return wkt_multipolygon($geom)
190             if $geom->isa('Geo::Surface');
191              
192             croak "ERROR: Cannot translate object $geom into SQL"
193             unless $geom->isa('Geo::Space');
194              
195             # Geo::Space
196              
197             return wkt_optimal($geom->component(0))
198             if $geom->nrComponents==1;
199              
200             $geom->onlyPoints ? wkt_multipoint($geom->points)
201             # remove these when I am sure all works
202             # : $geom->onlyRings ? wkt_multipolygon($geom->lines)
203             # : $geom->onlyLines ? wkt_multilinestring($geom->lines)
204             : wkt_geomcollection($geom)
205             }
206              
207              
208             sub wkt_geomcollection(@)
209             { @_ = $_[0]->components
210             if @_==1
211             && ref $_[0] ne 'ARRAY'
212             && $_[0]->isa('Geo::Space');
213              
214             'GEOMETRYCOLLECTION(' . join( ',', map { wkt_optimal $_ } @_ ) . ')';
215             }
216              
217             1;