Skip to content

Commit

Permalink
Implement point buffers for simple_spherical and simple_cartesian fac…
Browse files Browse the repository at this point in the history
…tories
  • Loading branch information
dazuma committed May 4, 2012
1 parent 906a638 commit 051f561
Show file tree
Hide file tree
Showing 18 changed files with 114 additions and 14 deletions.
6 changes: 6 additions & 0 deletions History.rdoc
@@ -1,3 +1,9 @@
=== 0.3.13 / 2012-05-04

* The spherical factory and the simple cartesian factory now support buffers around points (but not around other types). Accordingly, those factories now take the :buffer_resolution property argument.
* The :uses_lenient_assertions and parser/generator arguments to RGeo::Geographic.spherical_factory did not have their advertised effect. Fixed.
* The parser/generator arguments to projected geographic factories did not have their advertised effect. Fixed.

=== 0.3.12 / 2012-04-24

* Geos::FFIFactory collection constructors sometimes modified arguments in place, which caused problems for the ZMFactory among other things. Fixed.
Expand Down
2 changes: 1 addition & 1 deletion Version
@@ -1 +1 @@
0.3.12
0.3.13
2 changes: 1 addition & 1 deletion ext/geos_c_impl/extconf.rb
Expand Up @@ -71,7 +71,7 @@
header_dirs_, lib_dirs_ = dir_config('geos', header_dirs_, lib_dirs_)
if have_header('geos_c.h')
$libs << ' -lgeos -lgeos_c'
if have_func('initGEOS_r', 'geos_c.h')
if have_func('GEOSSetSRID_r', 'geos_c.h')
found_geos_ = true
else
$libs.gsub!(' -lgeos -lgeos_c', '')
Expand Down
2 changes: 1 addition & 1 deletion ext/geos_c_impl/preface.h
Expand Up @@ -36,7 +36,7 @@


#ifdef HAVE_GEOS_C_H
#ifdef HAVE_INITGEOS_R
#ifdef HAVE_GEOSSETSRID_R
#define RGEO_GEOS_SUPPORTED
#endif
#endif
Expand Down
8 changes: 8 additions & 0 deletions lib/rgeo/cartesian/factory.rb
Expand Up @@ -77,6 +77,8 @@ def initialize(opts_={})
srid_ ||= @coord_sys.authority_code if @coord_sys
@srid = srid_.to_i
@lenient_assertions = opts_[:uses_lenient_assertions] ? true : false
@buffer_resolution = opts_[:buffer_resolution].to_i
@buffer_resolution = 1 if @buffer_resolution < 1

wkt_generator_ = opts_[:wkt_generator]
case wkt_generator_
Expand Down Expand Up @@ -129,6 +131,7 @@ def marshal_dump # :nodoc:
'wktp' => @wkt_parser._properties,
'wkbp' => @wkb_parser._properties,
'lena' => @lenient_assertions,
'bufr' => @buffer_resolution,
}
hash_['proj4'] = @proj4.marshal_dump if @proj4
hash_['cs'] = @coord_sys.to_wkt if @coord_sys
Expand Down Expand Up @@ -156,6 +159,7 @@ def marshal_load(data_) # :nodoc:
:wkt_parser => ImplHelper::Utils.symbolize_hash(data_['wktp']),
:wkb_parser => ImplHelper::Utils.symbolize_hash(data_['wkbp']),
:uses_lenient_assertions => data_['lena'],
:buffer_resolution => data_['bufr'],
:proj4 => proj4_,
:coord_sys => coord_sys_
)
Expand All @@ -169,6 +173,7 @@ def encode_with(coder_) # :nodoc:
coder_['has_m_coordinate'] = @has_m
coder_['srid'] = @srid
coder_['lenient_assertions'] = @lenient_assertions
coder_['buffer_resolution'] = @buffer_resolution
coder_['wkt_generator'] = @wkt_generator._properties
coder_['wkb_generator'] = @wkb_generator._properties
coder_['wkt_parser'] = @wkt_parser._properties
Expand Down Expand Up @@ -204,6 +209,7 @@ def init_with(coder_) # :nodoc:
:wkt_parser => ImplHelper::Utils.symbolize_hash(coder_['wkt_parser']),
:wkb_parser => ImplHelper::Utils.symbolize_hash(coder_['wkb_parser']),
:uses_lenient_assertions => coder_['lenient_assertions'],
:buffer_resolution => coder_['buffer_resolution'],
:proj4 => proj4_,
:coord_sys => coord_sys_
)
Expand All @@ -227,6 +233,8 @@ def property(name_)
@has_m
when :uses_lenient_assertions
@lenient_assertions
when :buffer_resolution
@buffer_resolution
when :is_cartesian
true
else
Expand Down
11 changes: 11 additions & 0 deletions lib/rgeo/cartesian/feature_methods.rb
Expand Up @@ -71,6 +71,17 @@ def distance(rhs_)
end


def buffer(distance_)
point_count_ = factory.property(:buffer_resolution) * 4
angle_ = -::Math::PI * 2.0 / point_count_
points_ = (0...point_count_).map do |i_|
r_ = angle_ * i_
factory.point(@x + distance_ * ::Math.cos(r_), @y + distance_ * ::Math.sin(r_))
end
factory.polygon(factory.linear_ring(points_))
end


end


Expand Down
8 changes: 8 additions & 0 deletions lib/rgeo/geographic/factory.rb
Expand Up @@ -75,6 +75,8 @@ def initialize(impl_prefix_, opts_={}) # :nodoc:
@coord_sys = CoordSys::CS.create_from_wkt(@coord_sys) rescue nil
end
@lenient_assertions = opts_[:uses_lenient_assertions] ? true : false
@buffer_resolution = opts_[:buffer_resolution].to_i
@buffer_resolution = 1 if @buffer_resolution < 1

wkt_generator_ = opts_[:wkt_generator]
case wkt_generator_
Expand Down Expand Up @@ -138,6 +140,7 @@ def marshal_dump # :nodoc:
'wktp' => @wkt_parser._properties,
'wkbp' => @wkb_parser._properties,
'lena' => @lenient_assertions,
'bufr' => @buffer_resolution,
}
hash_['proj4'] = @proj4.marshal_dump if @proj4
hash_['cs'] = @coord_sys.to_wkt if @coord_sys
Expand Down Expand Up @@ -169,6 +172,7 @@ def marshal_load(data_) # :nodoc:
:wkt_parser => ImplHelper::Utils.symbolize_hash(data_['wktp']),
:wkb_parser => ImplHelper::Utils.symbolize_hash(data_['wkbp']),
:uses_lenient_assertions => data_['lena'],
:buffer_resolution => data_['bufr'],
:proj4 => proj4_,
:coord_sys => coord_sys_
)
Expand All @@ -195,6 +199,7 @@ def encode_with(coder_) # :nodoc:
coder_['wkt_parser'] = @wkt_parser._properties
coder_['wkb_parser'] = @wkb_parser._properties
coder_['lenient_assertions'] = @lenient_assertions
coder_['buffer_resolution'] = @buffer_resolution
if @proj4
str_ = @proj4.original_str || @proj4.canonical_str
coder_['proj4'] = @proj4.radians? ? {'proj4' => str_, 'radians' => true} : str_
Expand Down Expand Up @@ -230,6 +235,7 @@ def init_with(coder_) # :nodoc:
:wkt_parser => ImplHelper::Utils.symbolize_hash(coder_['wkt_parser']),
:wkb_parser => ImplHelper::Utils.symbolize_hash(coder_['wkb_parser']),
:uses_lenient_assertions => coder_['lenient_assertions'],
:buffer_resolution => coder_['buffer_resolution'],
:proj4 => proj4_,
:coord_sys => coord_sys_
)
Expand Down Expand Up @@ -334,6 +340,8 @@ def property(name_)
@support_m
when :uses_lenient_assertions
@lenient_assertions
when :buffer_resolution
@buffer_resolution
when :is_geographic
true
else
Expand Down
20 changes: 20 additions & 0 deletions lib/rgeo/geographic/interface.rb
Expand Up @@ -92,6 +92,16 @@ class << self
# Polygon and MultiPolygon. This may speed up creation of certain
# objects, at the expense of not doing the proper checking for
# OGC compliance. Default is false.
# [<tt>:buffer_resolution</tt>]
# The resolution of buffers around geometries created by this
# factory. This controls the number of line segments used to
# approximate curves. The default is 1, which causes, for
# example, the buffer around a point to be approximated by a
# 4-sided polygon. A resolution of 2 would cause that buffer
# to be approximated by an 8-sided polygon. The exact behavior
# for different kinds of buffers is not specified precisely,
# but in general the value is taken as the number of segments
# per 90-degree curve.
# [<tt>:proj4</tt>]
# Provide the coordinate system in Proj4 format. You may pass
# either an RGeo::CoordSys::Proj4 object, or a string or hash
Expand Down Expand Up @@ -153,6 +163,12 @@ def spherical_factory(opts_={})
:has_m_coordinate => opts_[:has_m_coordinate],
:proj4 => proj4_ || _proj4_4055,
:coord_sys => coord_sys_ || _coordsys_4055,
:uses_lenient_assertions => opts_[:uses_lenient_assertions],
:buffer_resolution => opts_[:buffer_resolution],
:wkt_parser => opts_[:wkt_parser],
:wkb_parser => opts_[:wkb_parser],
:wkt_generator => opts_[:wkt_generator],
:wkb_generator => opts_[:wkb_generator],
:srid => (srid_ || 4055).to_i)
end

Expand Down Expand Up @@ -234,6 +250,10 @@ def simple_mercator_factory(opts_={})
:proj4 => _proj4_4326,
:coord_sys => _coordsys_4326,
:srid => 4326,
:wkt_parser => opts_[:wkt_parser],
:wkb_parser => opts_[:wkb_parser],
:wkt_generator => opts_[:wkt_generator],
:wkb_generator => opts_[:wkb_generator],
:has_z_coordinate => opts_[:has_z_coordinate],
:has_m_coordinate => opts_[:has_m_coordinate])
projector_ = Geographic::SimpleMercatorProjector.new(factory_,
Expand Down
20 changes: 20 additions & 0 deletions lib/rgeo/geographic/spherical_feature_classes.rb
Expand Up @@ -96,6 +96,26 @@ def equals?(rhs_)
end


def buffer(distance_)
radius_ = distance_ / SphericalMath::RADIUS
radius_ = 1.5 if radius_ > 1.5
cos_ = ::Math.cos(radius_)
sin_ = ::Math.sin(radius_)
point_count_ = factory.property(:buffer_resolution) * 4
p0_ = _xyz
p1_ = p0_.create_perpendicular
p2_ = p1_ % p0_
angle_ = ::Math::PI * 2.0 / point_count_
points_ = (0...point_count_).map do |i_|
r_ = angle_ * i_
pi_ = SphericalMath::PointXYZ.weighted_combination(p1_, ::Math.cos(r_), p2_, ::Math.sin(r_))
p_ = SphericalMath::PointXYZ.weighted_combination(p0_, cos_, pi_, sin_)
factory.point(*p_.lonlat)
end
factory.polygon(factory.linear_ring(points_))
end


alias_method :longitude, :x
alias_method :lon, :x
alias_method :latitude, :y
Expand Down
28 changes: 28 additions & 0 deletions lib/rgeo/geographic/spherical_math.rb
Expand Up @@ -90,6 +90,14 @@ def latlon
end


def lonlat
lat_rad_ = ::Math.asin(@z)
lon_rad_ = ::Math.atan2(@y, @x) rescue 0.0
rpd_ = ImplHelper::Math::RADIANS_PER_DEGREE
[lon_rad_ / rpd_, lat_rad_ / rpd_]
end


def *(rhs_)
val_ = @x * rhs_.x + @y * rhs_.y + @z * rhs_.z
val_ = 1.0 if val_ > 1.0
Expand Down Expand Up @@ -119,6 +127,17 @@ def dist_to_point(rhs_)
end


# Creates some point that is perpendicular to this point

def create_perpendicular
p1dot_ = self * P1
p2dot_ = self * P2
p1dot_ = -p1dot_ if p1dot_ < 0
p2dot_ = -p2dot_ if p2dot_ < 0
p1dot_ < p2dot_ ? (self % P1) : (self % P2)
end


def self.from_latlon(lat_, lon_)
rpd_ = ImplHelper::Math::RADIANS_PER_DEGREE
lat_rad_ = rpd_ * lat_
Expand All @@ -130,6 +149,15 @@ def self.from_latlon(lat_, lon_)
new(x_, y_, z_)
end


def self.weighted_combination(p1_, w1_, p2_, w2_)
new(p1_.x * w1_ + p2_.x * w2_, p1_.y * w1_ + p2_.y * w2_, p1_.z * w1_ + p2_.z * w2_)
end


P1 = new(1, 0, 0)
P2 = new(0, 1, 0)

end


Expand Down
1 change: 1 addition & 0 deletions test/common/point_tests.rb
Expand Up @@ -286,6 +286,7 @@ def test_buffer
point_ = @factory.point(11, 12)
buffer_ = point_.buffer(4)
assert_equal(::RGeo::Feature::Polygon, buffer_.geometry_type)
assert_equal(33, buffer_.exterior_ring.num_points)
end


Expand Down
2 changes: 1 addition & 1 deletion test/geos_capi/tc_point.rb
Expand Up @@ -48,7 +48,7 @@ class TestPoint < ::Test::Unit::TestCase # :nodoc:


def setup
@factory = ::RGeo::Geos.factory
@factory = ::RGeo::Geos.factory(:buffer_resolution => 8)
@zfactory = ::RGeo::Geos.factory(:has_z_coordinate => true)
@mfactory = ::RGeo::Geos.factory(:has_m_coordinate => true)
@zmfactory = ::RGeo::Geos.factory(:has_z_coordinate => true, :has_m_coordinate => true)
Expand Down
2 changes: 1 addition & 1 deletion test/geos_ffi/tc_point.rb
Expand Up @@ -48,7 +48,7 @@ class TestPoint < ::Test::Unit::TestCase # :nodoc:


def setup
@factory = ::RGeo::Geos.factory(:native_interface => :ffi)
@factory = ::RGeo::Geos.factory(:native_interface => :ffi, :buffer_resolution => 8)
@zfactory = ::RGeo::Geos.factory(:has_z_coordinate => true, :native_interface => :ffi)
@mfactory = ::RGeo::Geos.factory(:has_m_coordinate => true, :native_interface => :ffi)
@zmfactory = ::RGeo::Geos.factory(:has_z_coordinate => true, :has_m_coordinate => true,
Expand Down
2 changes: 1 addition & 1 deletion test/projected_geographic/tc_point.rb
Expand Up @@ -48,7 +48,7 @@ class TestPoint < ::Test::Unit::TestCase # :nodoc:


def setup
@factory = ::RGeo::Geographic.projected_factory(:projection_proj4 => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', :projection_srid => 3857)
@factory = ::RGeo::Geographic.projected_factory(:buffer_resolution => 8, :projection_proj4 => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', :projection_srid => 3857)
@zfactory = ::RGeo::Geographic.projected_factory(:has_z_coordinate => true, :projection_proj4 => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', :projection_srid => 3857)
@mfactory = ::RGeo::Geographic.projected_factory(:has_m_coordinate => true, :projection_proj4 => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', :projection_srid => 3857)
@zmfactory = ::RGeo::Geographic.projected_factory(:has_z_coordinate => true, :has_m_coordinate => true, :projection_proj4 => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', :projection_srid => 3857)
Expand Down
3 changes: 1 addition & 2 deletions test/simple_cartesian/tc_point.rb
Expand Up @@ -48,7 +48,7 @@ class TestPoint < ::Test::Unit::TestCase # :nodoc:


def setup
@factory = ::RGeo::Cartesian.simple_factory(:srid => 1)
@factory = ::RGeo::Cartesian.simple_factory(:srid => 1, :buffer_resolution => 8)
@zfactory = ::RGeo::Cartesian.simple_factory(:srid => 1, :has_z_coordinate => true)
@mfactory = ::RGeo::Cartesian.simple_factory(:srid => 1, :has_m_coordinate => true)
@zmfactory = ::RGeo::Cartesian.simple_factory(:srid => 1, :has_z_coordinate => true, :has_m_coordinate => true)
Expand Down Expand Up @@ -82,7 +82,6 @@ def test_distance
undef_method :test_union
undef_method :test_difference
undef_method :test_sym_difference
undef_method :test_buffer


end
Expand Down
2 changes: 1 addition & 1 deletion test/simple_mercator/tc_point.rb
Expand Up @@ -48,7 +48,7 @@ class TestPoint < ::Test::Unit::TestCase # :nodoc:


def setup
@factory = ::RGeo::Geographic.simple_mercator_factory
@factory = ::RGeo::Geographic.simple_mercator_factory(:buffer_resolution => 8)
@zfactory = ::RGeo::Geographic.simple_mercator_factory(:has_z_coordinate => true)
@mfactory = ::RGeo::Geographic.simple_mercator_factory(:has_m_coordinate => true)
@zmfactory = ::RGeo::Geographic.simple_mercator_factory(:has_z_coordinate => true, :has_m_coordinate => true)
Expand Down
3 changes: 1 addition & 2 deletions test/spherical_geographic/tc_point.rb
Expand Up @@ -48,7 +48,7 @@ class TestPoint < ::Test::Unit::TestCase # :nodoc:


def setup
@factory = ::RGeo::Geographic.spherical_factory
@factory = ::RGeo::Geographic.spherical_factory(:buffer_resolution => 8)
@zfactory = ::RGeo::Geographic.spherical_factory(:has_z_coordinate => true)
@mfactory = ::RGeo::Geographic.spherical_factory(:has_m_coordinate => true)
@zmfactory = ::RGeo::Geographic.spherical_factory(:has_z_coordinate => true, :has_m_coordinate => true)
Expand Down Expand Up @@ -100,7 +100,6 @@ def test_floating_point_perturbation
undef_method :test_union
undef_method :test_difference
undef_method :test_sym_difference
undef_method :test_buffer


end
Expand Down
6 changes: 3 additions & 3 deletions test/tc_oneoff.rb
Expand Up @@ -46,10 +46,10 @@ class TestOneOff < ::Test::Unit::TestCase # :nodoc:

def setup
# @mercator_factory = ::RGeo::Geographic.simple_mercator_factory
# @spherical_factory = ::RGeo::Geographic.spherical_factory(:has_z_coordinate => true)
# @spherical_factory = ::RGeo::Geographic.spherical_factory(:buffer_resolution => 2)
# @projected_factory = ::RGeo::Geographic.projected_factory(:projection_proj4 => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', :projection_srid => 3857, :has_z_coordinate => true)
# @geos_factory = ::RGeo::Geos.factory(:buffer_resolution => 8)
# @cartesian_factory = ::RGeo::Cartesian.simple_factory(:srid => 1, :has_z_coordinate => true)
# @geos_factory = ::RGeo::Geos.factory(:buffer_resolution => 2)
# @cartesian_factory = ::RGeo::Cartesian.simple_factory(:buffer_resolution => 2)
end


Expand Down

0 comments on commit 051f561

Please sign in to comment.