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.
@@ -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.
@@ -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
- * \RGeo 0.2.8 or later.
42
- * The "dbf" gem, version 1.5.3 or later, is recommended. This gem is
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-2011 Daniel Azuma
80
+ Copyright 2010-2012 Daniel Azuma
83
81
 
84
82
  All rights reserved.
85
83
 
data/Version CHANGED
@@ -1 +1 @@
1
- 0.2.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'
@@ -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", ".idx", and ".dbf") must be present for
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!(/\.shp$/, '')
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
- dbf_record_ = @attr_dbf.record(@cur_record_index)
468
+ dbf_record_attrs_ = @attr_dbf.record(@cur_record_index).attributes
469
469
  @attr_dbf.columns.each do |col_|
470
- attrs_[col_.name] = dbf_record_.send(col_.underscored_name)
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