rgeo-shapefile 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +7 -0
- data/README.rdoc +3 -5
- data/Version +1 -1
- data/lib/rgeo-shapefile.rb +36 -0
- data/lib/rgeo/shapefile.rb +13 -13
- data/lib/rgeo/shapefile/reader.rb +164 -163
- data/lib/rgeo/shapefile/version.rb +12 -12
- data/test/shapelib_testcases/readme.txt +1 -1
- data/test/tc_basic.rb +13 -13
- data/test/tc_shapelib_tests.rb +56 -55
- metadata +40 -43
data/History.rdoc
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
=== 0.2.3 / 2012-01-09
|
2
|
+
|
3
|
+
* The dbf gem is now a hard dependency (now that it no longer pulls in ActiveSupport).
|
4
|
+
* Reader#initialize no longer tries to modify its parameters in place. (Reported by Brad Johnson.)
|
5
|
+
* DBF column reading should be more robust. (Possible fix for GH-4, reported by michael-groble.)
|
6
|
+
* Added an "rgeo-shapefile.rb" wrapper so bundler's auto-require will work without modification. (Reported by Mauricio Pasquier Juan.)
|
7
|
+
|
1
8
|
=== 0.2.2 / 2011-04-11
|
2
9
|
|
3
10
|
* A .gemspec file is now available for gem building and bundler git integration.
|
data/README.rdoc
CHANGED
@@ -38,10 +38,8 @@ Example:
|
|
38
38
|
RGeo::Shapefile has the following requirements:
|
39
39
|
|
40
40
|
* Ruby 1.8.7 or later. Ruby 1.9.2 or later preferred.
|
41
|
-
*
|
42
|
-
*
|
43
|
-
needed to read the attributes file. If it is not present, shapefiles
|
44
|
-
can still be read, but attributes will not be available.
|
41
|
+
* rgeo 0.3.3 or later.
|
42
|
+
* dbf 1.7 or later.
|
45
43
|
|
46
44
|
Install RGeo::Shapefile as a gem:
|
47
45
|
|
@@ -79,7 +77,7 @@ ESRI shapefiles, we did borrow a bunch of their test cases.
|
|
79
77
|
|
80
78
|
=== License
|
81
79
|
|
82
|
-
Copyright 2010-
|
80
|
+
Copyright 2010-2012 Daniel Azuma
|
83
81
|
|
84
82
|
All rights reserved.
|
85
83
|
|
data/Version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.3
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Shapefile processing for RGeo
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# Copyright 2010-2012 Daniel Azuma
|
7
|
+
#
|
8
|
+
# All rights reserved.
|
9
|
+
#
|
10
|
+
# Redistribution and use in source and binary forms, with or without
|
11
|
+
# modification, are permitted provided that the following conditions are met:
|
12
|
+
#
|
13
|
+
# * Redistributions of source code must retain the above copyright notice,
|
14
|
+
# this list of conditions and the following disclaimer.
|
15
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
16
|
+
# this list of conditions and the following disclaimer in the documentation
|
17
|
+
# and/or other materials provided with the distribution.
|
18
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
19
|
+
# contributors to this software, may be used to endorse or promote products
|
20
|
+
# derived from this software without specific prior written permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
# -----------------------------------------------------------------------------
|
34
|
+
;
|
35
|
+
|
36
|
+
require 'rgeo/shapefile'
|
data/lib/rgeo/shapefile.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# -----------------------------------------------------------------------------
|
2
|
-
#
|
2
|
+
#
|
3
3
|
# Shapefile processing for RGeo
|
4
|
-
#
|
4
|
+
#
|
5
5
|
# -----------------------------------------------------------------------------
|
6
|
-
# Copyright 2010 Daniel Azuma
|
7
|
-
#
|
6
|
+
# Copyright 2010-2012 Daniel Azuma
|
7
|
+
#
|
8
8
|
# All rights reserved.
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# Redistribution and use in source and binary forms, with or without
|
11
11
|
# modification, are permitted provided that the following conditions are met:
|
12
|
-
#
|
12
|
+
#
|
13
13
|
# * Redistributions of source code must retain the above copyright notice,
|
14
14
|
# this list of conditions and the following disclaimer.
|
15
15
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
@@ -18,7 +18,7 @@
|
|
18
18
|
# * Neither the name of the copyright holder, nor the names of any other
|
19
19
|
# contributors to this software, may be used to endorse or promote products
|
20
20
|
# derived from this software without specific prior written permission.
|
21
|
-
#
|
21
|
+
#
|
22
22
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
23
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
24
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
@@ -39,22 +39,22 @@ require 'rgeo'
|
|
39
39
|
|
40
40
|
|
41
41
|
# RGeo is a spatial data library for Ruby, provided by the "rgeo" gem.
|
42
|
-
#
|
42
|
+
#
|
43
43
|
# The optional RGeo::Shapefile module provides a set of tools for reading
|
44
44
|
# ESRI shapefiles.
|
45
45
|
|
46
46
|
module RGeo
|
47
|
-
|
48
|
-
|
47
|
+
|
48
|
+
|
49
49
|
# This module contains an implementation of ESRI Shapefiles.
|
50
50
|
# Use the Shapefile::Reader class to read a shapefile, extracting
|
51
51
|
# geometry and attribute data from it.
|
52
52
|
# RGeo does not yet have support for writing shapefiles.
|
53
|
-
|
53
|
+
|
54
54
|
module Shapefile
|
55
55
|
end
|
56
|
-
|
57
|
-
|
56
|
+
|
57
|
+
|
58
58
|
end
|
59
59
|
|
60
60
|
|
@@ -1,15 +1,15 @@
|
|
1
1
|
# -----------------------------------------------------------------------------
|
2
|
-
#
|
2
|
+
#
|
3
3
|
# Shapefile reader for RGeo
|
4
|
-
#
|
4
|
+
#
|
5
5
|
# -----------------------------------------------------------------------------
|
6
|
-
# Copyright 2010 Daniel Azuma
|
7
|
-
#
|
6
|
+
# Copyright 2010-2012 Daniel Azuma
|
7
|
+
#
|
8
8
|
# All rights reserved.
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# Redistribution and use in source and binary forms, with or without
|
11
11
|
# modification, are permitted provided that the following conditions are met:
|
12
|
-
#
|
12
|
+
#
|
13
13
|
# * Redistributions of source code must retain the above copyright notice,
|
14
14
|
# this list of conditions and the following disclaimer.
|
15
15
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
@@ -18,7 +18,7 @@
|
|
18
18
|
# * Neither the name of the copyright holder, nor the names of any other
|
19
19
|
# contributors to this software, may be used to endorse or promote products
|
20
20
|
# derived from this software without specific prior written permission.
|
21
|
-
#
|
21
|
+
#
|
22
22
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
23
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
24
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
@@ -41,28 +41,28 @@ end
|
|
41
41
|
|
42
42
|
|
43
43
|
module RGeo
|
44
|
-
|
44
|
+
|
45
45
|
module Shapefile
|
46
|
-
|
47
|
-
|
46
|
+
|
47
|
+
|
48
48
|
# Represents a shapefile that is open for reading.
|
49
|
-
#
|
49
|
+
#
|
50
50
|
# You can use this object to read a shapefile straight through,
|
51
51
|
# yielding the data in a block; or you can perform random access
|
52
52
|
# reads of indexed records.
|
53
|
-
#
|
53
|
+
#
|
54
54
|
# You must close this object after you are done, in order to close
|
55
55
|
# the underlying files. Alternatively, you can pass a block to
|
56
56
|
# Reader::open, and the reader will be closed automatically for
|
57
57
|
# you at the end of the block.
|
58
|
-
#
|
58
|
+
#
|
59
59
|
# === Dependencies
|
60
|
-
#
|
60
|
+
#
|
61
61
|
# Attributes in shapefiles are stored in a ".dbf" (dBASE) format
|
62
62
|
# file. The "dbf" gem is required to read these files. If this
|
63
63
|
# gem is not installed, shapefile reading will still function,
|
64
64
|
# but attributes will not be available.
|
65
|
-
#
|
65
|
+
#
|
66
66
|
# Correct interpretation of the polygon shape type requires some
|
67
67
|
# functionality that is available in the RGeo::Geos module. Hence,
|
68
68
|
# reading a polygon shapefile will generally fail if that module is
|
@@ -70,13 +70,13 @@ module RGeo
|
|
70
70
|
# to bypass this requirement by relaxing the polygon tests and making
|
71
71
|
# some assumptions about the file format. See the documentation for
|
72
72
|
# Reader::open for details.
|
73
|
-
#
|
73
|
+
#
|
74
74
|
# === Shapefile support
|
75
|
-
#
|
75
|
+
#
|
76
76
|
# This class supports shapefiles formatted according to the 1998
|
77
77
|
# "ESRI Shapefile Technical Description". It converts shapefile
|
78
78
|
# data to RGeo geometry objects, as follows:
|
79
|
-
#
|
79
|
+
#
|
80
80
|
# * Shapefile records are represented by the
|
81
81
|
# RGeo::Shapefile::Reader::Record class, which provides the
|
82
82
|
# geometry, the attributes, and the record number (0-based).
|
@@ -96,9 +96,9 @@ module RGeo
|
|
96
96
|
# * The multipatch shape type yields GeometryCollection geometries.
|
97
97
|
# (See below for an explanation of why we do not return a
|
98
98
|
# MultiPolygon.)
|
99
|
-
#
|
99
|
+
#
|
100
100
|
# Some special notes and limitations in our shapefile support:
|
101
|
-
#
|
101
|
+
#
|
102
102
|
# * Our implementation assumes that shapefile data is in a Cartesian
|
103
103
|
# coordinate system when it performs certain computations, such as
|
104
104
|
# directionality of polygon rings. It also ignores the 180 degree
|
@@ -131,35 +131,35 @@ module RGeo
|
|
131
131
|
# assertions, which do not allow constituent polygons to share a
|
132
132
|
# common boundary. Therefore, when reading a multipatch, we return
|
133
133
|
# a GeometryCollection instead of a MultiPolygon.
|
134
|
-
|
134
|
+
|
135
135
|
class Reader
|
136
|
-
|
137
|
-
|
136
|
+
|
137
|
+
|
138
138
|
# Values less than this value are considered "no value" in the
|
139
139
|
# shapefile format specification.
|
140
140
|
NODATA_LIMIT = -1e38
|
141
|
-
|
142
|
-
|
141
|
+
|
142
|
+
|
143
143
|
# Create a new shapefile reader. You must pass the path for the
|
144
144
|
# main shapefile (e.g. "path/to/file.shp"). You may also omit the
|
145
145
|
# ".shp" extension from the path. All three files that make up the
|
146
|
-
# shapefile (".shp", ".
|
146
|
+
# shapefile (".shp", ".shx", and ".dbf") must be present for
|
147
147
|
# successful opening of a shapefile.
|
148
|
-
#
|
148
|
+
#
|
149
149
|
# You must also provide a RGeo::Feature::FactoryGenerator. It should
|
150
150
|
# understand the configuration options <tt>:has_z_coordinate</tt>
|
151
151
|
# and <tt>:has_m_coordinate</tt>. You may also pass a specific
|
152
152
|
# RGeo::Feature::Factory, or nil to specify the default Cartesian
|
153
153
|
# FactoryGenerator.
|
154
|
-
#
|
154
|
+
#
|
155
155
|
# If you provide a block, the shapefile reader will be yielded to
|
156
156
|
# the block, and automatically closed at the end of the block.
|
157
157
|
# If you do not provide a block, the shapefile reader will be
|
158
158
|
# returned from this call. It is then the caller's responsibility
|
159
159
|
# to close the reader when it is done.
|
160
|
-
#
|
160
|
+
#
|
161
161
|
# Options include:
|
162
|
-
#
|
162
|
+
#
|
163
163
|
# [<tt>:factory_generator</tt>]
|
164
164
|
# A RGeo::Feature::FactoryGenerator that should return a factory
|
165
165
|
# based on the dimension settings in the input. It should
|
@@ -174,9 +174,9 @@ module RGeo
|
|
174
174
|
# [<tt>:assume_inner_follows_outer</tt>]
|
175
175
|
# If set to true, some assumptions are made about ring ordering
|
176
176
|
# in a polygon shapefile. See below for details. Default is false.
|
177
|
-
#
|
177
|
+
#
|
178
178
|
# === Ring ordering in polygon shapefiles
|
179
|
-
#
|
179
|
+
#
|
180
180
|
# The ESRI polygon shape type specifies that the ordering of rings
|
181
181
|
# in the shapefile is not significant. That is, rings can be in any
|
182
182
|
# order, and inner rings need not necessarily follow the outer ring
|
@@ -185,7 +185,7 @@ module RGeo
|
|
185
185
|
# it becomes necessary to run some geometric analysis on the rings
|
186
186
|
# that are read in, in order to determine which inner rings should
|
187
187
|
# go with which outer rings.
|
188
|
-
#
|
188
|
+
#
|
189
189
|
# RGeo's shapefile reader uses GEOS to perform this analysis.
|
190
190
|
# However, this means that if GEOS is not available, the analysis
|
191
191
|
# will fail. It also means reading polygons may be slow, especially
|
@@ -199,7 +199,7 @@ module RGeo
|
|
199
199
|
# is not turned on by default. However, if you are running RGeo on
|
200
200
|
# a platform without GEOS, you have no choice but to turn on this
|
201
201
|
# switch and make this assumption about your input shapefiles.
|
202
|
-
|
202
|
+
|
203
203
|
def self.open(path_, opts_={}, &block_)
|
204
204
|
file_ = new(path_, opts_)
|
205
205
|
if block_
|
@@ -213,14 +213,14 @@ module RGeo
|
|
213
213
|
file_
|
214
214
|
end
|
215
215
|
end
|
216
|
-
|
217
|
-
|
216
|
+
|
217
|
+
|
218
218
|
# Low-level creation of a Reader. The arguments are the same as
|
219
219
|
# those passed to Reader::open, except that this doesn't take a
|
220
220
|
# block. You should use Reader::open instead.
|
221
|
-
|
221
|
+
|
222
222
|
def initialize(path_, opts_={}) # :nodoc:
|
223
|
-
path_.sub
|
223
|
+
path_ = path_.sub(/\.shp$/, '')
|
224
224
|
@base_path = path_
|
225
225
|
@opened = true
|
226
226
|
@main_file = ::File.open(path_+'.shp', 'rb:ascii-8bit')
|
@@ -235,7 +235,7 @@ module RGeo
|
|
235
235
|
index_length_ = @index_file.read(100).unpack('x24Nx72').first
|
236
236
|
@num_records = (index_length_ - 50) / 4
|
237
237
|
@cur_record_index = 0
|
238
|
-
|
238
|
+
|
239
239
|
if @num_records == 0
|
240
240
|
@xmin = @xmax = @ymin = @ymax = @zmin = @zmax = @mmin = @mmax = nil
|
241
241
|
else
|
@@ -253,7 +253,7 @@ module RGeo
|
|
253
253
|
@mmin = @mmax = @zmin = @zmax = nil
|
254
254
|
end
|
255
255
|
end
|
256
|
-
|
256
|
+
|
257
257
|
@factory = opts_[:factory_generator] || opts_[:factory] || Cartesian.method(:preferred_factory)
|
258
258
|
unless @factory.kind_of?(Feature::Factory::Instance)
|
259
259
|
factory_config_ = {}
|
@@ -268,15 +268,15 @@ module RGeo
|
|
268
268
|
end
|
269
269
|
@factory_supports_z = @factory.property(:has_z_coordinate)
|
270
270
|
@factory_supports_m = @factory.property(:has_m_coordinate)
|
271
|
-
|
271
|
+
|
272
272
|
@assume_inner_follows_outer = opts_[:assume_inner_follows_outer]
|
273
273
|
end
|
274
|
-
|
275
|
-
|
274
|
+
|
275
|
+
|
276
276
|
# Close the shapefile.
|
277
277
|
# You should not use this Reader after it has been closed.
|
278
278
|
# Most methods will return nil.
|
279
|
-
|
279
|
+
|
280
280
|
def close
|
281
281
|
if @opened
|
282
282
|
@main_file.close
|
@@ -285,131 +285,131 @@ module RGeo
|
|
285
285
|
@opened = false
|
286
286
|
end
|
287
287
|
end
|
288
|
-
|
289
|
-
|
288
|
+
|
289
|
+
|
290
290
|
# Returns true if this Reader is still open, or false if it has
|
291
291
|
# been closed.
|
292
|
-
|
292
|
+
|
293
293
|
def open?
|
294
294
|
@opened
|
295
295
|
end
|
296
|
-
|
297
|
-
|
296
|
+
|
297
|
+
|
298
298
|
# Returns true if attributes are available. This may be false
|
299
299
|
# because there is no ".dbf" file or because the dbf gem is not
|
300
300
|
# available.
|
301
|
-
|
301
|
+
|
302
302
|
def attributes_available?
|
303
303
|
@opened ? (@attr_dbf ? true : false) : nil
|
304
304
|
end
|
305
|
-
|
306
|
-
|
305
|
+
|
306
|
+
|
307
307
|
# Returns the factory used by this reader.
|
308
|
-
|
308
|
+
|
309
309
|
def factory
|
310
310
|
@opened ? @factory : nil
|
311
311
|
end
|
312
|
-
|
313
|
-
|
312
|
+
|
313
|
+
|
314
314
|
# Returns the number of records in the shapefile.
|
315
|
-
|
315
|
+
|
316
316
|
def num_records
|
317
317
|
@opened ? @num_records : nil
|
318
318
|
end
|
319
319
|
alias_method :size, :num_records
|
320
|
-
|
321
|
-
|
320
|
+
|
321
|
+
|
322
322
|
# Returns the shape type code.
|
323
|
-
|
323
|
+
|
324
324
|
def shape_type_code
|
325
325
|
@opened ? @shape_type_code : nil
|
326
326
|
end
|
327
|
-
|
328
|
-
|
327
|
+
|
328
|
+
|
329
329
|
# Returns the minimum x.
|
330
|
-
|
330
|
+
|
331
331
|
def xmin
|
332
332
|
@opened ? @xmin : nil
|
333
333
|
end
|
334
|
-
|
335
|
-
|
334
|
+
|
335
|
+
|
336
336
|
# Returns the maximum x.
|
337
|
-
|
337
|
+
|
338
338
|
def xmax
|
339
339
|
@opened ? @xmax : nil
|
340
340
|
end
|
341
|
-
|
342
|
-
|
341
|
+
|
342
|
+
|
343
343
|
# Returns the minimum y.
|
344
|
-
|
344
|
+
|
345
345
|
def ymin
|
346
346
|
@opened ? @ymin : nil
|
347
347
|
end
|
348
|
-
|
349
|
-
|
348
|
+
|
349
|
+
|
350
350
|
# Returns the maximum y.
|
351
|
-
|
351
|
+
|
352
352
|
def ymax
|
353
353
|
@opened ? @ymax : nil
|
354
354
|
end
|
355
|
-
|
356
|
-
|
355
|
+
|
356
|
+
|
357
357
|
# Returns the minimum z, or nil if the shapefile does not contain z.
|
358
|
-
|
358
|
+
|
359
359
|
def zmin
|
360
360
|
@opened ? @zmin : nil
|
361
361
|
end
|
362
|
-
|
363
|
-
|
362
|
+
|
363
|
+
|
364
364
|
# Returns the maximum z, or nil if the shapefile does not contain z.
|
365
|
-
|
365
|
+
|
366
366
|
def zmax
|
367
367
|
@opened ? @zmax : nil
|
368
368
|
end
|
369
|
-
|
370
|
-
|
369
|
+
|
370
|
+
|
371
371
|
# Returns the minimum m, or nil if the shapefile does not contain m.
|
372
|
-
|
372
|
+
|
373
373
|
def mmin
|
374
374
|
@opened ? @mmin : nil
|
375
375
|
end
|
376
|
-
|
377
|
-
|
376
|
+
|
377
|
+
|
378
378
|
# Returns the maximum m, or nil if the shapefile does not contain m.
|
379
|
-
|
379
|
+
|
380
380
|
def mmax
|
381
381
|
@opened ? @mmax : nil
|
382
382
|
end
|
383
|
-
|
384
|
-
|
383
|
+
|
384
|
+
|
385
385
|
# Returns the current file pointer as a record index (0-based).
|
386
386
|
# This is the record number that will be read when Reader#next
|
387
387
|
# is called.
|
388
|
-
|
388
|
+
|
389
389
|
def cur_index
|
390
390
|
@opened ? @cur_record_index : nil
|
391
391
|
end
|
392
|
-
|
393
|
-
|
392
|
+
|
393
|
+
|
394
394
|
# Read and return the next record as a Reader::Record.
|
395
|
-
|
395
|
+
|
396
396
|
def next
|
397
397
|
@opened && @cur_record_index < @num_records ? _read_next_record : nil
|
398
398
|
end
|
399
|
-
|
400
|
-
|
399
|
+
|
400
|
+
|
401
401
|
# Read the remaining records starting with the current record index,
|
402
402
|
# and yield the Reader::Record for each one.
|
403
|
-
|
403
|
+
|
404
404
|
def each
|
405
405
|
while @cur_record_index < @num_records
|
406
406
|
yield _read_next_record
|
407
407
|
end if @opened
|
408
408
|
end
|
409
|
-
|
410
|
-
|
409
|
+
|
410
|
+
|
411
411
|
# Seek to the given record index.
|
412
|
-
|
412
|
+
|
413
413
|
def seek_index(index_)
|
414
414
|
if @opened && index_ >= 0 && index_ <= @num_records
|
415
415
|
if index_ < @num_records && index_ != @cur_record_index
|
@@ -423,25 +423,25 @@ module RGeo
|
|
423
423
|
false
|
424
424
|
end
|
425
425
|
end
|
426
|
-
|
427
|
-
|
426
|
+
|
427
|
+
|
428
428
|
# Rewind to the beginning of the file.
|
429
429
|
# Equivalent to seek_index(0).
|
430
|
-
|
430
|
+
|
431
431
|
def rewind
|
432
432
|
seek_index(0)
|
433
433
|
end
|
434
|
-
|
435
|
-
|
434
|
+
|
435
|
+
|
436
436
|
# Get the given record number. Equivalent to seeking to that index
|
437
437
|
# and calling next.
|
438
|
-
|
438
|
+
|
439
439
|
def get(index_)
|
440
440
|
seek_index(index_) ? self.next : nil
|
441
441
|
end
|
442
442
|
alias_method :[], :get
|
443
|
-
|
444
|
-
|
443
|
+
|
444
|
+
|
445
445
|
def _read_next_record # :nodoc:
|
446
446
|
num_, length_ = @main_file.read(8).unpack('NN')
|
447
447
|
data_ = @main_file.read(length_ * 2)
|
@@ -465,17 +465,18 @@ module RGeo
|
|
465
465
|
end
|
466
466
|
attrs_ = {}
|
467
467
|
if @attr_dbf
|
468
|
-
|
468
|
+
dbf_record_attrs_ = @attr_dbf.record(@cur_record_index).attributes
|
469
469
|
@attr_dbf.columns.each do |col_|
|
470
|
-
|
470
|
+
name_ = col_.name
|
471
|
+
attrs_[name_] = dbf_record_attrs_[name_]
|
471
472
|
end
|
472
473
|
end
|
473
474
|
result_ = Record.new(@cur_record_index, geometry_, attrs_)
|
474
475
|
@cur_record_index += 1
|
475
476
|
result_
|
476
477
|
end
|
477
|
-
|
478
|
-
|
478
|
+
|
479
|
+
|
479
480
|
def _read_point(data_, opt_=nil) # :nodoc:
|
480
481
|
case opt_
|
481
482
|
when :z
|
@@ -493,18 +494,18 @@ module RGeo
|
|
493
494
|
extras_ << m_ if @factory_supports_m
|
494
495
|
@factory.point(x_, y_, *extras_)
|
495
496
|
end
|
496
|
-
|
497
|
-
|
497
|
+
|
498
|
+
|
498
499
|
def _read_multipoint(data_, opt_=nil) # :nodoc:
|
499
500
|
# Read number of points
|
500
501
|
num_points_ = data_[36,4].unpack('V').first
|
501
|
-
|
502
|
+
|
502
503
|
# Read remaining data
|
503
504
|
size_ = num_points_*16
|
504
505
|
size_ += 16 + num_points_*8 if opt_
|
505
506
|
size_ += 16 + num_points_*8 if opt_ == :z
|
506
507
|
values_ = data_[40, size_].unpack('E*')
|
507
|
-
|
508
|
+
|
508
509
|
# Extract XY, Z, and M values
|
509
510
|
xys_ = values_.slice!(0, num_points_*2)
|
510
511
|
ms_ = nil
|
@@ -517,7 +518,7 @@ module RGeo
|
|
517
518
|
ms_.map!{ |val_| val_ < NODATA_LIMIT ? 0 : val_ } if ms_
|
518
519
|
end
|
519
520
|
end
|
520
|
-
|
521
|
+
|
521
522
|
# Generate points
|
522
523
|
points_ = (0..num_points_-1).map do |i_|
|
523
524
|
extras_ = []
|
@@ -529,21 +530,21 @@ module RGeo
|
|
529
530
|
# Return a MultiPoint
|
530
531
|
@factory.multi_point(points_)
|
531
532
|
end
|
532
|
-
|
533
|
-
|
533
|
+
|
534
|
+
|
534
535
|
def _read_polyline(data_, opt_=nil) # :nodoc:
|
535
536
|
# Read counts
|
536
537
|
num_parts_, num_points_ = data_[36,8].unpack('VV')
|
537
|
-
|
538
|
+
|
538
539
|
# Read remaining data
|
539
540
|
size_ = num_parts_*4 + num_points_*16
|
540
541
|
size_ += 16 + num_points_*8 if opt_
|
541
542
|
size_ += 16 + num_points_*8 if opt_ == :z
|
542
543
|
values_ = data_[44, size_].unpack("V#{num_parts_}E*")
|
543
|
-
|
544
|
+
|
544
545
|
# Parts array
|
545
546
|
part_indexes_ = values_.slice!(0, num_parts_) + [num_points_]
|
546
|
-
|
547
|
+
|
547
548
|
# Extract XY, Z, and M values
|
548
549
|
xys_ = values_.slice!(0, num_points_*2)
|
549
550
|
ms_ = nil
|
@@ -556,7 +557,7 @@ module RGeo
|
|
556
557
|
ms_.map!{ |val_| val_ < NODATA_LIMIT ? 0 : val_ }
|
557
558
|
end
|
558
559
|
end
|
559
|
-
|
560
|
+
|
560
561
|
# Generate points
|
561
562
|
points_ = (0..num_points_-1).map do |i_|
|
562
563
|
extras_ = []
|
@@ -564,30 +565,30 @@ module RGeo
|
|
564
565
|
extras_ << ms_[i_] if ms_ && @factory_supports_m
|
565
566
|
@factory.point(xys_[i_*2], xys_[i_*2+1], *extras_)
|
566
567
|
end
|
567
|
-
|
568
|
+
|
568
569
|
# Generate LineString objects (parts)
|
569
570
|
parts_ = (0..num_parts_-1).map do |i_|
|
570
571
|
@factory.line_string(points_[part_indexes_[i_]...part_indexes_[i_+1]])
|
571
572
|
end
|
572
|
-
|
573
|
+
|
573
574
|
# Generate MultiLineString
|
574
575
|
@factory.multi_line_string(parts_)
|
575
576
|
end
|
576
|
-
|
577
|
-
|
577
|
+
|
578
|
+
|
578
579
|
def _read_polygon(data_, opt_=nil) # :nodoc:
|
579
580
|
# Read counts
|
580
581
|
num_parts_, num_points_ = data_[36,8].unpack('VV')
|
581
|
-
|
582
|
+
|
582
583
|
# Read remaining data
|
583
584
|
size_ = num_parts_*4 + num_points_*16
|
584
585
|
size_ += 16 + num_points_*8 if opt_
|
585
586
|
size_ += 16 + num_points_*8 if opt_ == :z
|
586
587
|
values_ = data_[44, size_].unpack("V#{num_parts_}E*")
|
587
|
-
|
588
|
+
|
588
589
|
# Parts array
|
589
590
|
part_indexes_ = values_.slice!(0, num_parts_) + [num_points_]
|
590
|
-
|
591
|
+
|
591
592
|
# Extract XY, Z, and M values
|
592
593
|
xys_ = values_.slice!(0, num_points_*2)
|
593
594
|
ms_ = nil
|
@@ -600,7 +601,7 @@ module RGeo
|
|
600
601
|
ms_.map!{ |val_| val_ < NODATA_LIMIT ? 0 : val_ } if ms_
|
601
602
|
end
|
602
603
|
end
|
603
|
-
|
604
|
+
|
604
605
|
# Generate points
|
605
606
|
points_ = (0..num_points_-1).map do |i_|
|
606
607
|
extras_ = []
|
@@ -608,12 +609,12 @@ module RGeo
|
|
608
609
|
extras_ << ms_[i_] if ms_ && @factory_supports_m
|
609
610
|
@factory.point(xys_[i_*2], xys_[i_*2+1], *extras_)
|
610
611
|
end
|
611
|
-
|
612
|
+
|
612
613
|
# The parts are LinearRing objects
|
613
614
|
parts_ = (0..num_parts_-1).map do |i_|
|
614
615
|
@factory.linear_ring(points_[part_indexes_[i_]...part_indexes_[i_+1]])
|
615
616
|
end
|
616
|
-
|
617
|
+
|
617
618
|
# Get a GEOS factory if needed.
|
618
619
|
geos_factory_ = nil
|
619
620
|
unless @assume_inner_follows_outer
|
@@ -622,7 +623,7 @@ module RGeo
|
|
622
623
|
raise Error::RGeoError, "GEOS is not available, but is required for correct interpretation of polygons in shapefiles."
|
623
624
|
end
|
624
625
|
end
|
625
|
-
|
626
|
+
|
626
627
|
# Special case: if there's only one part, treat it as an outer
|
627
628
|
# ring, regardless of its direction. This isn't strictly compliant
|
628
629
|
# with the shapefile spec, but the shapelib test cases seem to
|
@@ -630,14 +631,14 @@ module RGeo
|
|
630
631
|
if parts_.size == 1
|
631
632
|
return @factory.multi_polygon([@factory.polygon(parts_[0])])
|
632
633
|
end
|
633
|
-
|
634
|
+
|
634
635
|
# Collect some data on the rings: the ring direction, a GEOS
|
635
636
|
# polygon (for intersection calculation), and an initial guess
|
636
637
|
# of which polygon index the ring belongs to.
|
637
638
|
parts_.map! do |ring_|
|
638
639
|
[ring_, Cartesian::Analysis.ring_direction(ring_) < 0, geos_factory_ ? geos_factory_.polygon(ring_) : nil, nil]
|
639
640
|
end
|
640
|
-
|
641
|
+
|
641
642
|
# Initial population of the polygon data array.
|
642
643
|
# Each element is an array of the part data for the rings, first
|
643
644
|
# the outer ring and then the inner rings.
|
@@ -653,7 +654,7 @@ module RGeo
|
|
653
654
|
end
|
654
655
|
part_data_[3] = polygons_.size - 1
|
655
656
|
end
|
656
|
-
|
657
|
+
|
657
658
|
# If :assume_inner_follows_outer is in effect, we assume this
|
658
659
|
# initial guess is the correct one, and we don't run the
|
659
660
|
# potentially expensive intersection tests.
|
@@ -704,37 +705,37 @@ module RGeo
|
|
704
705
|
end
|
705
706
|
end
|
706
707
|
end
|
707
|
-
|
708
|
+
|
708
709
|
# Generate the actual polygons from the collected polygon data
|
709
710
|
polygons_.map! do |poly_data_|
|
710
711
|
outer_ = poly_data_[0][0]
|
711
712
|
inner_ = poly_data_[1..-1].map{ |part_data_| part_data_[0] }
|
712
713
|
@factory.polygon(outer_, inner_)
|
713
714
|
end
|
714
|
-
|
715
|
+
|
715
716
|
# Finally, return the MultiPolygon.
|
716
717
|
@factory.multi_polygon(polygons_)
|
717
718
|
end
|
718
|
-
|
719
|
-
|
719
|
+
|
720
|
+
|
720
721
|
def _read_multipatch(data_) # :nodoc:
|
721
722
|
# Read counts
|
722
723
|
num_parts_, num_points_ = data_[36,8].unpack('VV')
|
723
|
-
|
724
|
+
|
724
725
|
# Read remaining data
|
725
726
|
values_ = data_[44, 32 + num_parts_*8 + num_points_*32].unpack("V#{num_parts_*2}E*")
|
726
|
-
|
727
|
+
|
727
728
|
# Parts arrays
|
728
729
|
part_indexes_ = values_.slice!(0, num_parts_) + [num_points_]
|
729
730
|
part_types_ = values_.slice!(0, num_parts_)
|
730
|
-
|
731
|
+
|
731
732
|
# Extract XY, Z, and M values
|
732
733
|
xys_ = values_.slice!(0, num_points_*2)
|
733
734
|
zs_ = values_.slice!(2, num_points_)
|
734
735
|
zs_.map!{ |val_| val_ < NODATA_LIMIT ? 0 : val_ } if zs_
|
735
736
|
ms_ = values_.slice!(4, num_points_)
|
736
737
|
ms_.map!{ |val_| val_ < NODATA_LIMIT ? 0 : val_ } if ms_
|
737
|
-
|
738
|
+
|
738
739
|
# Generate points
|
739
740
|
points_ = (0..num_points_-1).map do |i_|
|
740
741
|
extras_ = []
|
@@ -742,7 +743,7 @@ module RGeo
|
|
742
743
|
extras_ << ms_[i_] if ms_ && @factory_supports_m
|
743
744
|
@factory.point(xys_[i_*2], xys_[i_*2+1], *extras_)
|
744
745
|
end
|
745
|
-
|
746
|
+
|
746
747
|
# Create the parts
|
747
748
|
parts_ = (0..num_parts_-1).map do |i_|
|
748
749
|
ps_ = points_[part_indexes_[i_]...part_indexes_[i_+1]]
|
@@ -765,7 +766,7 @@ module RGeo
|
|
765
766
|
end
|
766
767
|
@factory.linear_ring(ps_)
|
767
768
|
end
|
768
|
-
|
769
|
+
|
769
770
|
# Get a GEOS factory if needed.
|
770
771
|
geos_factory_ = nil
|
771
772
|
unless @assume_inner_follows_outer
|
@@ -774,7 +775,7 @@ module RGeo
|
|
774
775
|
raise Error::RGeoError, "GEOS is not available, but is required for correct interpretation of polygons in shapefiles."
|
775
776
|
end
|
776
777
|
end
|
777
|
-
|
778
|
+
|
778
779
|
# Walk the parts and generate polygons
|
779
780
|
polygons_ = []
|
780
781
|
state_ = :empty
|
@@ -785,7 +786,7 @@ module RGeo
|
|
785
786
|
(0..num_parts_).each do |index_|
|
786
787
|
part_ = parts_[index_]
|
787
788
|
type_ = part_types_[index_]
|
788
|
-
|
789
|
+
|
789
790
|
# This section handles any state.
|
790
791
|
# It either stays in the state and goes to the next part,
|
791
792
|
# or it wraps up the state. Either way, at the end of this
|
@@ -839,7 +840,7 @@ module RGeo
|
|
839
840
|
sequence_ = []
|
840
841
|
end
|
841
842
|
end
|
842
|
-
|
843
|
+
|
843
844
|
# State is now :empty. We allow any type except 3 (since an
|
844
845
|
# (inner must come during an outer-led sequence).
|
845
846
|
# We treat a type 5 ring that isn't part of a first-led sequence
|
@@ -855,52 +856,52 @@ module RGeo
|
|
855
856
|
state_ = :first
|
856
857
|
end
|
857
858
|
end
|
858
|
-
|
859
|
+
|
859
860
|
# Return the geometry as a collection.
|
860
861
|
@factory.collection(polygons_)
|
861
862
|
end
|
862
|
-
|
863
|
-
|
863
|
+
|
864
|
+
|
864
865
|
# Shapefile records are provided to the caller as objects of this
|
865
866
|
# type. The record includes the record index (0-based), the
|
866
867
|
# geometry (which may be nil if the shape type is the null type),
|
867
868
|
# and a hash of attributes from the associated dbf file.
|
868
|
-
#
|
869
|
+
#
|
869
870
|
# You should not need to create objects of this type yourself.
|
870
|
-
|
871
|
+
|
871
872
|
class Record
|
872
|
-
|
873
|
+
|
873
874
|
def initialize(index_, geometry_, attributes_) # :nodoc:
|
874
875
|
@index = index_
|
875
876
|
@geometry = geometry_
|
876
877
|
@attributes = attributes_
|
877
878
|
end
|
878
|
-
|
879
|
+
|
879
880
|
# The 0-based record number
|
880
881
|
attr_reader :index
|
881
|
-
|
882
|
+
|
882
883
|
# The geometry contained in this shapefile record
|
883
884
|
attr_reader :geometry
|
884
|
-
|
885
|
+
|
885
886
|
# The attributes as a hash.
|
886
887
|
attr_reader :attributes
|
887
|
-
|
888
|
+
|
888
889
|
# Returns an array of keys for all this record's attributes.
|
889
890
|
def keys
|
890
891
|
@attributes.keys
|
891
892
|
end
|
892
|
-
|
893
|
+
|
893
894
|
# Returns the value for the given attribute key.
|
894
895
|
def [](key_)
|
895
|
-
@attributes[key_]
|
896
|
+
@attributes[key_.to_s]
|
896
897
|
end
|
897
|
-
|
898
|
+
|
898
899
|
end
|
899
|
-
|
900
|
-
|
900
|
+
|
901
|
+
|
901
902
|
end
|
902
|
-
|
903
|
-
|
903
|
+
|
904
|
+
|
904
905
|
end
|
905
|
-
|
906
|
+
|
906
907
|
end
|