# -----------------------------------------------------------------------------
#
# Access to geographic data factories
#
# -----------------------------------------------------------------------------
# Copyright 2010 Daniel Azuma
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of the copyright holder, nor the names of any other
# contributors to this software, may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# -----------------------------------------------------------------------------
;
module RGeo
module Geography
class << self
# Creates and returns a geographic factory that does not include a
# a projection, and which performs calculations assuming a
# spherical earth. In other words, geodesics are treated as great
# circle arcs, and geometric calculations are handled accordingly.
# Size and distance calculations report results in meters.
# This implementation is thus ideal for everyday calculations on
# the globe in which good accuracy is desired, but in which it is
# not deemed necessary to perform the complex ellipsoidal
# calculations needed for greater precision.
#
# The maximum error is about 0.5 percent, for objects and
# calculations that span a significant percentage of the globe, due
# to distortion caused by rotational flattening of the earth. For
# calculations that span a much smaller area, the error can drop to
# a few meters or less.
#
# === Limitations
#
# This implementation does not implement some of the more advanced
# geometric operations. In particular:
#
# * Relational operators such as Feature::Geometry#intersects? are
# not implemented for most types.
# * Relational constructors such as Feature::Geometry#union are
# not implemented for most types.
# * Buffer, convex hull, and envelope calculations are not
# implemented for most types. Boundaries are available except for
# GeometryCollection.
# * Length calculations are available, but areas are not. Distances
# are available only between points.
# * Equality and simplicity evaluation are implemented for some but
# not all types.
# * Assertions for polygons and multipolygons are not implemented.
#
# Unimplemented operations will return nil if invoked.
#
# === Options
#
# You may use the following options when creating a spherical
# factory:
#
# :support_z_coordinate::
# Support z_coordinate. Default is false.
# :support_m_coordinate::
# Support m_coordinate. Default is false.
# :proj4::
# Provide the coordinate system in Proj4 format. You may pass
# either an RGeo::CoordSys::Proj4 object, or a string or hash
# containing the Proj4 parameters. This coordinate system must be
# a geographic (lat/long) coordinate system. The default is the
# "popular visualization CRS" (EPSG 4055), represented by
# "+proj=longlat +a=6378137 +b=6378137 +towgs84=0,0,0,0,0,0,0 +no_defs".
# Has no effect if Proj4 is not available.
# :srid::
# The SRID that should be returned by features from this factory.
# Default is 4055, indicating EPSG 4055, the "popular
# visualization crs". You may alternatively wish to set the srid
# to 4326, indicating the WGS84 crs, but note that that value
# implies an ellipsoidal datum, not a spherical datum.
def spherical(opts_={})
Geography::Factory.new('Spherical', :support_z_coordinate => opts_[:support_z_coordinate], :support_m_coordinate => opts_[:support_m_coordinate], :proj4 => opts_[:proj4] || '+proj=longlat +a=6378137 +b=6378137 +towgs84=0,0,0,0,0,0,0 +no_defs', :srid => opts_[:srid] || 4055)
end
# Creates and returns a geographic factory that is designed for
# visualization applications that use Google or Bing maps, or any
# other visualization systems that use the same projection. It
# includes a projection factory that matches the projection used
# by those mapping systems.
#
# Like all geographic factories, this one creates features using
# latitude-longitude values. However, calculations such as
# intersections are done in the projected coordinate system, and
# size and distance calculations report results in the projected
# units.
#
# The behavior of the simple_mercator factory could also be obtained
# using a projected with appropriate Proj4 specifications. However,
# the simple_mercator implementation is done without actually
# requiring the Proj4 library. The projections are simple enough to
# be implemented in pure ruby.
#
# === About the coordinate system
#
# Many popular visualization technologies, such as Google and Bing
# maps, actually use two coordinate systems. The first is the
# standard WSG84 lat-long system used by the GPS and represented
# by EPSG 4326. Most API calls and input-output in these mapping
# technologies utilize this coordinate system. The second is a
# Mercator projection based on a "sphericalization" of the WGS84
# lat-long system. This projection is the basis of the map's screen
# and tiling coordinates, and has been assigned EPSG 3857.
#
# This factory represents both coordinate systems. The main factory
# produces data in the lat-long system and reports SRID 4326, and
# the projected factory produces data in the projection and reports
# SRID 3857. Latitudes are restricted to the range
# (-85.05112877980659, 85.05112877980659), which conveniently
# results in a square projected domain.
#
# === Options
#
# You may use the following options when creating a simple_mercator
# factory:
#
# :lenient_multi_polygon_assertions::
# If set to true, assertion checking on MultiPolygon is disabled.
# This may speed up creation of MultiPolygon objects, at the
# expense of not doing the proper checking for OGC MultiPolygon
# compliance. See RGeo::Feature::MultiPolygon for details on
# the MultiPolygon assertions. Default is false.
# :buffer_resolution::
# 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 defined by GEOS.
# :support_z_coordinate::
# Support z_coordinate. Default is false.
# Note that simple_mercator factories cannot support both
# z_coordinate and m_coordinate. They may at
# most support one or the other.
# :support_m_coordinate::
# Support m_coordinate. Default is false.
# Note that simple_mercator factories cannot support both
# z_coordinate and m_coordinate. They may at
# most support one or the other.
def simple_mercator(opts_={})
factory_ = Geography::Factory.new('Projected', :proj4 => '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs', :srid => 4326, :support_z_coordinate => opts_[:support_z_coordinate], :support_m_coordinate => opts_[:support_m_coordinate])
projector_ = Geography::SimpleMercatorProjector.new(factory_, :buffer_resolution => opts_[:buffer_resolution], :lenient_multi_polygon_assertions => opts_[:lenient_multi_polygon_assertions], :support_z_coordinate => opts_[:support_z_coordinate], :support_m_coordinate => opts_[:support_m_coordinate])
factory_._set_projector(projector_)
factory_
end
# Creates and returns a geographic factory that includes a
# projection specified by a Proj4 coordinate system. Like all
# geographic factories, this one creates features using latitude-
# longitude values. However, calculations such as intersections are
# done in the projected coordinate system, and size and distance
# calculations report results in the projected units.
#
# This implementation is intended for advanced GIS applications
# requiring intimate control over the projection being used.
#
# === Options
#
# When creating a projected implementation, you must provide either
# the :projection_factory option, indicating an existing
# Cartesian factory to use for the projection, or the
# :projection_proj4 option, indicating a Proj4 projection
# to use to construct an appropriate projection factory.
#
# If you provide :projection_factory, the following options
# are supported.
#
# :projection_factory::
# Specify an existing Cartesian factory to use for the projection.
# This factory must support the :proj4 capability.
#
# Note that in this case, the geography factory's z-coordinate and
# m-coordinate availability will be set to match the projection's
# z-coordinate and m-coordinate availability.
#
# If you provide :projection_proj4, the following options
# are supported.
#
# :projection_proj4::
# Specify a Proj4 projection to use. This may be specified as a
# CoordSys::Proj4 object, or as a Proj4 string or hash
# representation.
# :projection_srid::
# An SRID value to use for the projection factory. Default is 0.
# :lenient_multi_polygon_assertions::
# If set to true, assertion checking on MultiPolygon is disabled.
# This may speed up creation of MultiPolygon objects, at the
# expense of not doing the proper checking for OGC MultiPolygon
# compliance. See RGeo::Feature::MultiPolygon for details on
# the MultiPolygon assertions. Default is false.
# :buffer_resolution::
# 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 defined by GEOS.
# :support_z_coordinate::
# Support z_coordinate. Default is false.
# :support_m_coordinate::
# Support m_coordinate. Default is false.
def projected(opts_={})
unless CoordSys::Proj4.supported?
raise Error::UnsupportedCapability, "Proj4 is not supported because the proj4 library was not found at install time."
end
if (projection_factory_ = opts_[:projection_factory])
factory_ = Geography::Factory.new('Projected', :proj4 => opts_[:proj4] || '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs', :srid => opts_[:srid] || 4326, :support_z_coordinate => projection_factory_.has_capability?(:z_coordinate), :support_m_coordinate => projection_factory_.has_capability?(:m_coordinate))
projector_ = Geography::Proj4Projector.create_from_existing_factory(factory_, projection_factory_)
elsif opts_[:projection_proj4]
factory_ = Geography::Factory.new('Projected', :proj4 => opts_[:proj4] || '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs', :srid => opts_[:srid] || 4326, :support_z_coordinate => opts_[:support_z_coordinate], :support_m_coordinate => opts_[:support_m_coordinate])
projector_ = Geography::Proj4Projector.create_from_proj4(factory_, opts_[:projection_proj4], :srid => opts_[:projection_srid], :buffer_resolution => opts_[:buffer_resolution], :lenient_multi_polygon_assertions => opts_[:lenient_multi_polygon_assertions], :support_z_coordinate => opts_[:support_z_coordinate], :support_m_coordinate => opts_[:support_m_coordinate])
else
raise ::ArgumentError, 'You must provide either :projection_proj4 or :projection_factory.'
end
factory_._set_projector(projector_)
factory_
end
end
end
end