rgeo-shapefile 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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