class GeoRuby::Shp4r::ShpTransaction
An object returned from ShpFile#transaction
. Buffers updates to a Shapefile
Attributes
rollbacked[R]
Public Class Methods
new(shp, dbf)
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 369 def initialize(shp, dbf) @deleted = {} @added = [] @shp = shp @dbf = dbf end
Public Instance Methods
add(record)
click to toggle source
add a ShpRecord
at the end
# File lib/geo_ruby/shp4r/shp.rb, line 389 def add(record) record_type = to_shp_type(record.geometry) fail IncompatibleGeometryException.new('Incompatible type') unless record_type == @shp.shp_type @added << record end
commit()
click to toggle source
updates the physical files
# File lib/geo_ruby/shp4r/shp.rb, line 396 def commit @shp.close @shp_r = open(@shp.file_root + '.shp', 'rb') @dbf_r = open(@shp.file_root + '.dbf', 'rb') @shp_io = open(@shp.file_root + '.shp.tmp.shp', 'wb') @shx_io = open(@shp.file_root + '.shx.tmp.shx', 'wb') @dbf_io = open(@shp.file_root + '.dbf.tmp.dbf', 'wb') index = commit_delete min_x, max_x, min_y, max_y, min_z, max_z, min_m, max_m = commit_add(index) commit_finalize(min_x, max_x, min_y, max_y, min_z, max_z, min_m, max_m) @shp_r.close @dbf_r.close @dbf_io.close @shp_io.close @shx_io.close FileUtils.move(@shp.file_root + '.shp.tmp.shp', @shp.file_root + '.shp') FileUtils.move(@shp.file_root + '.shx.tmp.shx', @shp.file_root + '.shx') FileUtils.move(@shp.file_root + '.dbf.tmp.dbf', @shp.file_root + '.dbf') @deleted = {} @added = [] @shp.reload! end
delete(i)
click to toggle source
delete a record. Does not take into account the records added in the current transaction
# File lib/geo_ruby/shp4r/shp.rb, line 377 def delete(i) fail UnexistantRecordException.new("Invalid index : #{i}") if @shp.record_count <= i @deleted[i] = true end
rollback()
click to toggle source
prevents the udpate from taking place
# File lib/geo_ruby/shp4r/shp.rb, line 422 def rollback @deleted = {} @added = [] @rollbacked = true end
update(i, record)
click to toggle source
Update a record. In effect just a delete followed by an add.
# File lib/geo_ruby/shp4r/shp.rb, line 383 def update(i, record) delete(i) add(record) end
Private Instance Methods
build_multi_point(geometry, str)
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 697 def build_multi_point(geometry, str) str << [geometry.length].pack('V') geometry.each do |point| str << [point.x, point.y].pack('E2') end str end
build_multi_point_zm(geometry, zm, range, str)
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 705 def build_multi_point_zm(geometry, zm, range, str) str << range.pack('E2') geometry.each do |point| str << [point.instance_variable_get(zm)].pack('E') end str end
build_polygon(geometry, str)
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 644 def build_polygon(geometry, str) if geometry.is_a? GeoRuby::SimpleFeatures::Polygon str << [geometry.length, geometry.reduce(0) { |a, e| a + e.length } ].pack('V2') # last element of the previous array is dropped str << geometry.reduce([0]) do |a, e| a << (a.last + e.length) end.pack("V#{geometry.length}") geometry.each do |lr| lr.each do |point| str << [point.x, point.y].pack('E2') end end else # multipolygon num_rings = geometry.reduce(0) { |a, e| a + e.length } str << [num_rings, geometry.reduce(0) { |l, poly| l + poly.reduce(0) { |l2, lr| l2 + lr.length } }].pack('V2') # last element of the previous array is dropped str << geometry.reduce([0]) do |a, e| e.reduce(a) { |a2, lr| a2 << (a2.last + lr.length) } end.pack("V#{num_rings}") geometry.each do |poly| poly.each do |lr| lr.each do |point| str << [point.x, point.y].pack('E2') end end end end str end
build_polygon_zm(geometry, zm, range, str)
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 677 def build_polygon_zm(geometry, zm, range, str) str << range.pack('E2') if geometry.is_a? GeoRuby::SimpleFeatures::Polygon geometry.each do |lr| lr.each do |point| str << [point.instance_variable_get(zm)].pack('E') end end else geometry.each do |poly| poly.each do |lr| lr.each do |point| str << [point.instance_variable_get(zm)].pack('E') end end end end str end
build_polyline(geometry, str)
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 606 def build_polyline(geometry, str) if geometry.is_a? GeoRuby::SimpleFeatures::LineString str << [1, geometry.length, 0].pack('V3') geometry.each do |point| str << [point.x, point.y].pack('E2') end else # multilinestring str << [geometry.length, geometry.reduce(0) { |a, e| a + e.length }].pack('V2') str << geometry.reduce([0]) do |a, e| a << (a.last + e.length) # last element of the previous array is dropped end.pack("V#{geometry.length}") geometry.each do |ls| ls.each do |point| str << [point.x, point.y].pack('E2') end end end str end
build_polyline_zm(geometry, zm, range, str)
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 627 def build_polyline_zm(geometry, zm, range, str) str << range.pack('E2') if geometry.is_a? GeoRuby::SimpleFeatures::LineString geometry.each do |point| str << [point.instance_variable_get(zm)].pack('E') end else # multilinestring geometry.each do |ls| ls.each do |point| str << [point.instance_variable_get(zm)].pack('E') end end end str end
build_shp_geometry(geometry)
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 541 def build_shp_geometry(geometry) m_range = nil answer = case @shp.shp_type when ShpType::POINT bbox = geometry.bounding_box [geometry.x, geometry.y].pack('E2') when ShpType::POLYLINE str, bbox = create_bbox(geometry) build_polyline(geometry, str) when ShpType::POLYGON str, bbox = create_bbox(geometry) build_polygon(geometry, str) when ShpType::MULTIPOINT str, bbox = create_bbox(geometry) build_multi_point(geometry, str) when ShpType::POINTZ bbox = geometry.bounding_box [geometry.x, geometry.y, geometry.z, geometry.m].pack('E4') when ShpType::POLYLINEZ str, bbox = create_bbox(geometry) m_range = geometry.m_range build_polyline(geometry, str) build_polyline_zm(geometry, :@z, [bbox[0].z, bbox[1].z], str) build_polyline_zm(geometry, :@m, m_range, str) when ShpType::POLYGONZ str, bbox = create_bbox(geometry) m_range = geometry.m_range build_polygon(geometry, str) build_polygon_zm(geometry, :@z, [bbox[0].z, bbox[1].z], str) build_polygon_zm(geometry, :@m, m_range, str) when ShpType::MULTIPOINTZ str, bbox = create_bbox(geometry) m_range = geometry.m_range build_multi_point(geometry, str) build_multi_point_zm(geometry, :@z, [bbox[0].z, bbox[1].z], str) build_multi_point_zm(geometry, :@m, m_range, str) when ShpType::POINTM bbox = geometry.bounding_box [geometry.x, geometry.y, geometry.m].pack('E3') when ShpType::POLYLINEM str, bbox = create_bbox(geometry) m_range = geometry.m_range build_polyline(geometry, str) build_polyline_zm(geometry, :@m, m_range, str) when ShpType::POLYGONM str, bbox = create_bbox(geometry) m_range = geometry.m_range build_polygon(geometry, str) build_polygon_zm(geometry, :@m, m_range, str) when ShpType::MULTIPOINTM str, bbox = create_bbox(geometry) m_range = geometry.m_range build_multi_point(geometry, str) build_multi_point_zm(geometry, :@m, m_range, str) end m_range ||= [0, 0] [answer, bbox[0].x, bbox[1].x, bbox[0].y, bbox[1].y, bbox[0].z || 0, bbox[1].z || 0, m_range[0], m_range[1]] end
commit_add(index)
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 449 def commit_add(index) max_x, min_x, max_y, min_y, max_z, min_z, max_m, min_m = @shp.xmax, @shp.xmin, @shp.ymax, @shp.ymin, @shp.zmax, @shp.zmin, @shp.mmax, @shp.mmin @added.each do |record| @dbf_io << ['20'].pack('H2') @dbf.fields.each do |field| data = record.data[field.name] str = if field.type == 'D' sprintf('%04i%02i%02i', data.year, data.month, data.mday) elsif field.type == 'L' data ? 'T' : 'F' else data.to_s end @dbf_io << [str].pack("A#{field.length}") end shp_str, min_xp, max_xp, min_yp, max_yp, min_zp, max_zp, min_mp, max_mp = build_shp_geometry(record.geometry) max_x = max_xp if max_xp > max_x min_x = min_xp if min_xp < min_x max_y = max_yp if max_yp > max_y min_y = min_yp if min_yp < min_y max_z = max_zp if max_zp > max_z min_z = min_zp if min_zp < min_z max_m = max_mp if max_mp > max_m min_m = min_mp if min_mp < min_m length = (shp_str.length / 2 + 2).to_i # num of 16-bit words; geom type is included (+2) @shx_io << [(@shp_io.pos / 2).to_i, length].pack('N2') @shp_io << [index, length, @shp.shp_type].pack('N2V') @shp_io << shp_str index += 1 end @shp_io.flush @shx_io.flush @dbf_io.flush [min_x, max_x, min_y, max_y, min_z, max_z, min_m, max_m] end
commit_delete()
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 486 def commit_delete @shp_r.rewind header = @shp_r.read(100) @shp_io << header @shx_io << header index = 1 until @shp_r.eof? icur, length = @shp_r.read(8).unpack('N2') unless @deleted[icur - 1] @shx_io << [(@shp_io.pos / 2).to_i, length].pack('N2') @shp_io << [index, length].pack('N2') @shp_io << @shp_r.read(length * 2) index += 1 else @shp_r.seek(length * 2, IO::SEEK_CUR) end end @shp_io.flush @shx_io.flush @dbf_r.rewind @dbf_io << @dbf_r.read(@dbf.header_length) icur = 0 until @dbf_r.eof? unless @deleted[icur] @dbf_io << @dbf_r.read(@dbf.record_length) else @dbf_r.seek(@dbf.record_length, IO::SEEK_CUR) end icur += 1 end @dbf_io.flush index end
commit_finalize(min_x, max_x, min_y, max_y, min_z, max_z, min_m, max_m)
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 521 def commit_finalize(min_x, max_x, min_y, max_y, min_z, max_z, min_m, max_m) # update size in shp and dbf + extent and num records in dbf @shp_io.seek(0, IO::SEEK_END) shp_size = @shp_io.pos / 2 @shx_io.seek(0, IO::SEEK_END) shx_size = @shx_io.pos / 2 @shp_io.seek(24) @shp_io.write([shp_size].pack('N')) @shx_io.seek(24) @shx_io.write([shx_size].pack('N')) @shp_io.seek(36) @shx_io.seek(36) str = [min_x, min_y, max_x, max_y, min_z, max_z, min_m, max_m].pack('E8') @shp_io.write(str) @shx_io.write(str) @dbf_io.seek(4) @dbf_io.write([@dbf.record_count + @added.length - @deleted.length].pack('V')) end
create_bbox(geometry)
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 601 def create_bbox(geometry) bbox = geometry.bounding_box [[bbox[0].x, bbox[0].y, bbox[1].x, bbox[1].y].pack('E4'), bbox] end
geom_type(geom)
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 430 def geom_type(geom) case geom when GeoRuby::SimpleFeatures::Point then 'POINT' when GeoRuby::SimpleFeatures::LineString then 'POLYLINE' when GeoRuby::SimpleFeatures::Polygon then 'POLYGON' when GeoRuby::SimpleFeatures::MultiPoint then 'MULTIPOINT' when GeoRuby::SimpleFeatures::MultiLineString then 'POLYLINE' when GeoRuby::SimpleFeatures::MultiPolygon then 'POLYGON' else false end end
to_shp_type(geom)
click to toggle source
# File lib/geo_ruby/shp4r/shp.rb, line 442 def to_shp_type(geom) return false unless klass = geom_type(geom) klass += 'Z' if geom.with_z klass += 'M' if geom.with_m GeoRuby::Shp4r.const_get('ShpType::' + klass) end