line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Geo::Shapefile::Writer; |
2
|
|
|
|
|
|
|
{ |
3
|
|
|
|
|
|
|
$Geo::Shapefile::Writer::VERSION = '0.005'; |
4
|
|
|
|
|
|
|
} |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
# $Id: Writer.pm 16 2014-07-30 08:16:24Z xliosha@gmail.com $ |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# NAME: Geo::Shapefile::Writer |
9
|
|
|
|
|
|
|
# ABSTRACT: simple pureperl shapefile writer |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
|
12
|
2
|
|
|
2
|
|
129989
|
use 5.010; |
|
2
|
|
|
|
|
8
|
|
|
2
|
|
|
|
|
87
|
|
13
|
2
|
|
|
2
|
|
13
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
68
|
|
14
|
2
|
|
|
2
|
|
11
|
use warnings; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
65
|
|
15
|
|
|
|
|
|
|
|
16
|
2
|
|
|
2
|
|
9
|
use utf8; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
16
|
|
17
|
2
|
|
|
2
|
|
1954
|
use autodie; |
|
2
|
|
|
|
|
41263
|
|
|
2
|
|
|
|
|
15
|
|
18
|
2
|
|
|
2
|
|
13760
|
use Carp; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
164
|
|
19
|
|
|
|
|
|
|
|
20
|
2
|
|
|
2
|
|
3788
|
use XBase; |
|
2
|
|
|
|
|
41737
|
|
|
2
|
|
|
|
|
101
|
|
21
|
2
|
|
|
2
|
|
27
|
use List::Util qw/ min max /; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
4088
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
my %shape_type = ( |
26
|
|
|
|
|
|
|
# extend |
27
|
|
|
|
|
|
|
NULL => 0, |
28
|
|
|
|
|
|
|
POINT => 1, |
29
|
|
|
|
|
|
|
POLYLINE => 3, |
30
|
|
|
|
|
|
|
POLYGON => 5, |
31
|
|
|
|
|
|
|
); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
{ |
36
|
|
|
|
|
|
|
my @default_attr_format = ( C => 64 ); |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub _get_attr_format { |
39
|
5
|
|
|
5
|
|
11
|
my ($format) = @_; |
40
|
|
|
|
|
|
|
|
41
|
5
|
50
|
|
|
|
29
|
my @descr = !ref $format ? ($format) |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
42
|
|
|
|
|
|
|
: ref $format eq 'ARRAY' ? @$format |
43
|
|
|
|
|
|
|
: ref $format eq 'HASH' ? @$format{ qw/ name type length decimals / } |
44
|
|
|
|
|
|
|
: (); |
45
|
|
|
|
|
|
|
|
46
|
5
|
100
|
|
|
|
37
|
croak 'Bad format description' if !$descr[0]; |
47
|
|
|
|
|
|
|
|
48
|
4
|
100
|
|
|
|
55
|
@descr[1,2] = @default_attr_format if !$descr[1]; |
49
|
4
|
|
|
|
|
14
|
return \@descr; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub new { |
54
|
4
|
|
|
4
|
1
|
4692
|
my ($class, $name, $type, @attrs) = @_; |
55
|
|
|
|
|
|
|
|
56
|
4
|
|
50
|
|
|
28
|
my $shape_type = $shape_type{ uc($type || q{}) }; |
57
|
4
|
100
|
|
|
|
39
|
croak "Invalid shape type: $type" if !defined $shape_type; |
58
|
|
|
|
|
|
|
|
59
|
3
|
|
|
|
|
24
|
my $self = bless { |
60
|
|
|
|
|
|
|
NAME => $name, |
61
|
|
|
|
|
|
|
TYPE => $shape_type, |
62
|
|
|
|
|
|
|
RCOUNT => 0, |
63
|
|
|
|
|
|
|
SHP_SIZE => 50, |
64
|
|
|
|
|
|
|
SHX_SIZE => 50, |
65
|
|
|
|
|
|
|
}, $class; |
66
|
|
|
|
|
|
|
|
67
|
3
|
|
|
|
|
18
|
my $header_data = $self->_get_header('SHP'); |
68
|
|
|
|
|
|
|
|
69
|
3
|
|
|
|
|
35
|
open $self->{SHP}, '>:raw', "$name.shp"; |
70
|
3
|
|
|
|
|
7490
|
print {$self->{SHP}} $header_data; |
|
3
|
|
|
|
|
35
|
|
71
|
|
|
|
|
|
|
|
72
|
3
|
|
|
|
|
20
|
open $self->{SHX}, '>:raw', "$name.shx"; |
73
|
3
|
|
|
|
|
512
|
print {$self->{SHX}} $header_data; |
|
3
|
|
|
|
|
40
|
|
74
|
|
|
|
|
|
|
|
75
|
3
|
50
|
|
|
|
74
|
unlink "$name.dbf" if -f "$name.dbf"; |
76
|
|
|
|
|
|
|
|
77
|
3
|
|
|
|
|
12
|
my @fields = map { _get_attr_format($_) } @attrs; |
|
5
|
|
|
|
|
13
|
|
78
|
4
|
|
|
|
|
11
|
$self->{DBF} = XBase->create( |
79
|
|
|
|
|
|
|
name => "$name.dbf", |
80
|
4
|
|
|
|
|
11
|
field_names => [ map { $_->[0] } @fields ], |
81
|
4
|
|
|
|
|
8
|
field_types => [ map { $_->[1] } @fields ], |
82
|
4
|
|
|
|
|
41
|
field_lengths => [ map { $_->[2] } @fields ], |
83
|
2
|
|
|
|
|
8
|
field_decimals => [ map { $_->[3] } @fields ], |
84
|
|
|
|
|
|
|
); |
85
|
|
|
|
|
|
|
|
86
|
2
|
|
|
|
|
2617
|
return $self; |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
{ |
91
|
|
|
|
|
|
|
my $header_size = 100; |
92
|
|
|
|
|
|
|
# position, pack_type, object_field, default |
93
|
|
|
|
|
|
|
my @header_fields = ( |
94
|
|
|
|
|
|
|
[ 0, 'N', undef, 9994 ], # magic |
95
|
|
|
|
|
|
|
[ 24, 'N', _SIZE => $header_size / 2 ], # file size in 16-bit words |
96
|
|
|
|
|
|
|
[ 28, 'L', undef, 1000 ], # version |
97
|
|
|
|
|
|
|
[ 32, 'L', 'TYPE' ], |
98
|
|
|
|
|
|
|
[ 36, 'd', 'XMIN' ], |
99
|
|
|
|
|
|
|
[ 44, 'd', 'YMIN' ], |
100
|
|
|
|
|
|
|
[ 52, 'd', 'XMAX' ], |
101
|
|
|
|
|
|
|
[ 60, 'd', 'YMAX' ], |
102
|
|
|
|
|
|
|
); |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub _get_header { |
105
|
7
|
|
|
7
|
|
15
|
my ($self, $file_type) = @_; |
106
|
|
|
|
|
|
|
|
107
|
56
|
|
|
|
|
107
|
my @use_fields = |
108
|
56
|
|
66
|
|
|
461
|
grep { defined $_->[2] } |
|
|
|
100
|
|
|
|
|
109
|
7
|
|
|
|
|
18
|
map {[ $_->[0], $_->[1], $_->[2] && ($self->{$_->[2]} // $self->{"$file_type$_->[2]"}) // $_->[3] ]} |
110
|
|
|
|
|
|
|
@header_fields; |
111
|
|
|
|
|
|
|
|
112
|
7
|
|
|
|
|
32
|
my $pack_string = join q{ }, map { sprintf '@%d%s', @$_ } (@use_fields, [$header_size, q{}]); |
|
51
|
|
|
|
|
149
|
|
113
|
7
|
|
|
|
|
22
|
return pack $pack_string, map { $_->[2] } @use_fields; |
|
44
|
|
|
|
|
117
|
|
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub add_shape { |
120
|
4
|
|
|
4
|
1
|
50
|
my ($self, $data, @attributes) = @_; |
121
|
|
|
|
|
|
|
|
122
|
4
|
|
|
|
|
6
|
my ($xmin, $ymin, $xmax, $ymax); |
123
|
|
|
|
|
|
|
|
124
|
0
|
|
|
|
|
0
|
my $rdata; |
125
|
4
|
|
|
|
|
11
|
my $type = $self->{TYPE}; |
126
|
|
|
|
|
|
|
|
127
|
4
|
50
|
33
|
|
|
33
|
if ($type == $shape_type{NULL} ) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
128
|
0
|
|
|
|
|
0
|
$rdata = pack( 'L', $self->{TYPE} ); |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
elsif ($type == $shape_type{POINT} ) { |
131
|
2
|
|
|
|
|
8
|
$rdata = pack( 'Ldd', $self->{TYPE}, @$data ); |
132
|
2
|
|
|
|
|
6
|
($xmin, $ymin, $xmax, $ymax) = ( @$data, @$data ); |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
elsif ($type == $shape_type{POLYLINE} || $type == $shape_type{POLYGON} ) { |
135
|
2
|
|
|
|
|
4
|
my $rpart = q{}; |
136
|
2
|
|
|
|
|
3
|
my $rpoint = q{}; |
137
|
2
|
|
|
|
|
3
|
my $ipoint = 0; |
138
|
|
|
|
|
|
|
|
139
|
2
|
|
|
|
|
5
|
for my $line ( @$data ) { |
140
|
3
|
|
|
|
|
7
|
$rpart .= pack 'L', $ipoint; |
141
|
3
|
|
|
|
|
5
|
for my $point ( @$line ) { |
142
|
7
|
|
|
|
|
10
|
my ($x, $y) = @$point; |
143
|
7
|
|
|
|
|
16
|
$rpoint .= pack 'dd', $x, $y; |
144
|
7
|
|
|
|
|
13
|
$ipoint ++; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
2
|
|
|
|
|
5
|
$xmin = min map {$_->[0]} map {@$_} @$data; |
|
7
|
|
|
|
|
28
|
|
|
3
|
|
|
|
|
7
|
|
149
|
2
|
|
|
|
|
5
|
$ymin = min map {$_->[1]} map {@$_} @$data; |
|
7
|
|
|
|
|
13
|
|
|
3
|
|
|
|
|
6
|
|
150
|
2
|
|
|
|
|
5
|
$xmax = max map {$_->[0]} map {@$_} @$data; |
|
7
|
|
|
|
|
13
|
|
|
3
|
|
|
|
|
6
|
|
151
|
2
|
|
|
|
|
5
|
$ymax = max map {$_->[1]} map {@$_} @$data; |
|
7
|
|
|
|
|
13
|
|
|
3
|
|
|
|
|
4
|
|
152
|
|
|
|
|
|
|
|
153
|
2
|
|
|
|
|
10
|
$rdata = pack 'LddddLL', $self->{TYPE}, $xmin, $ymin, $xmax, $ymax, scalar @$data, $ipoint; |
154
|
2
|
|
|
|
|
6
|
$rdata .= $rpart . $rpoint; |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
|
158
|
4
|
|
|
|
|
8
|
my $attr0 = $attributes[0]; |
159
|
4
|
100
|
|
|
|
29
|
if ( ref $attr0 eq 'HASH' ) { |
|
|
50
|
|
|
|
|
|
160
|
1
|
|
|
|
|
5
|
$self->{DBF}->set_record_hash( $self->{RCOUNT}, map {( uc($_) => $attr0->{$_} )} keys %$attr0 ); |
|
2
|
|
|
|
|
25
|
|
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
elsif ( ref $attr0 eq 'ARRAY' ) { |
163
|
0
|
|
|
|
|
0
|
$self->{DBF}->set_record( $self->{RCOUNT}, @$attr0 ); |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
else { |
166
|
3
|
|
|
|
|
23
|
$self->{DBF}->set_record( $self->{RCOUNT}, @attributes ); |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
4
|
|
|
|
|
1080
|
$self->{RCOUNT} ++; |
170
|
|
|
|
|
|
|
|
171
|
4
|
|
|
|
|
6
|
print {$self->{SHX}} pack 'NN', $self->{SHP_SIZE}, length($rdata)/2; |
|
4
|
|
|
|
|
21
|
|
172
|
4
|
|
|
|
|
9
|
$self->{SHX_SIZE} += 4; |
173
|
|
|
|
|
|
|
|
174
|
4
|
|
|
|
|
6
|
print {$self->{SHP}} pack 'NN', $self->{RCOUNT}, length($rdata)/2; |
|
4
|
|
|
|
|
16
|
|
175
|
4
|
|
|
|
|
6
|
print {$self->{SHP}} $rdata; |
|
4
|
|
|
|
|
8
|
|
176
|
4
|
|
|
|
|
9
|
$self->{SHP_SIZE} += 4+length($rdata)/2; |
177
|
|
|
|
|
|
|
|
178
|
4
|
|
|
|
|
10
|
$self->{XMIN} = min grep {defined} ($xmin, $self->{XMIN}); |
|
8
|
|
|
|
|
38
|
|
179
|
4
|
|
|
|
|
8
|
$self->{YMIN} = min grep {defined} ($ymin, $self->{YMIN}); |
|
8
|
|
|
|
|
23
|
|
180
|
4
|
|
|
|
|
10
|
$self->{XMAX} = max grep {defined} ($xmax, $self->{XMAX}); |
|
8
|
|
|
|
|
21
|
|
181
|
4
|
|
|
|
|
8
|
$self->{YMAX} = max grep {defined} ($ymax, $self->{YMAX}); |
|
8
|
|
|
|
|
20
|
|
182
|
|
|
|
|
|
|
|
183
|
4
|
|
|
|
|
14
|
return $self; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub finalize { |
189
|
2
|
|
|
2
|
1
|
18
|
my $self = shift; |
190
|
|
|
|
|
|
|
|
191
|
2
|
|
|
|
|
5
|
my $shp = $self->{SHP}; |
192
|
2
|
|
|
|
|
11
|
seek $shp, 0, 0; |
193
|
2
|
|
|
|
|
1480
|
print {$shp} $self->_get_header('SHP'); |
|
2
|
|
|
|
|
11
|
|
194
|
2
|
|
|
|
|
10
|
close $shp; |
195
|
|
|
|
|
|
|
|
196
|
2
|
|
|
|
|
2000
|
my $shx = $self->{SHX}; |
197
|
2
|
|
|
|
|
11
|
seek $shx, 0, 0; |
198
|
2
|
|
|
|
|
164
|
print {$shx} $self->_get_header('SHX'); |
|
2
|
|
|
|
|
11
|
|
199
|
2
|
|
|
|
|
11
|
close $shx; |
200
|
|
|
|
|
|
|
|
201
|
2
|
|
|
|
|
142
|
$self->{DBF}->close(); |
202
|
|
|
|
|
|
|
|
203
|
2
|
|
|
|
|
77
|
return; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
1; |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
__END__ |