rgeo-dschee 0.5.4
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +29 -0
- data/ext/geos_c_impl/coordinates.c +65 -0
- data/ext/geos_c_impl/coordinates.h +2 -0
- data/ext/geos_c_impl/extconf.rb +43 -0
- data/ext/geos_c_impl/factory.c +995 -0
- data/ext/geos_c_impl/factory.h +238 -0
- data/ext/geos_c_impl/geometry.c +1093 -0
- data/ext/geos_c_impl/geometry.h +23 -0
- data/ext/geos_c_impl/geometry_collection.c +757 -0
- data/ext/geos_c_impl/geometry_collection.h +46 -0
- data/ext/geos_c_impl/line_string.c +675 -0
- data/ext/geos_c_impl/line_string.h +32 -0
- data/ext/geos_c_impl/main.c +40 -0
- data/ext/geos_c_impl/point.c +236 -0
- data/ext/geos_c_impl/point.h +30 -0
- data/ext/geos_c_impl/polygon.c +359 -0
- data/ext/geos_c_impl/polygon.h +43 -0
- data/ext/geos_c_impl/preface.h +38 -0
- data/ext/proj4_c_impl/extconf.rb +62 -0
- data/ext/proj4_c_impl/main.c +315 -0
- data/lib/rgeo.rb +89 -0
- data/lib/rgeo/cartesian.rb +25 -0
- data/lib/rgeo/cartesian/analysis.rb +77 -0
- data/lib/rgeo/cartesian/bounding_box.rb +398 -0
- data/lib/rgeo/cartesian/calculations.rb +113 -0
- data/lib/rgeo/cartesian/factory.rb +347 -0
- data/lib/rgeo/cartesian/feature_classes.rb +100 -0
- data/lib/rgeo/cartesian/feature_methods.rb +88 -0
- data/lib/rgeo/cartesian/interface.rb +135 -0
- data/lib/rgeo/coord_sys.rb +43 -0
- data/lib/rgeo/coord_sys/cs/entities.rb +1315 -0
- data/lib/rgeo/coord_sys/cs/factories.rb +148 -0
- data/lib/rgeo/coord_sys/cs/wkt_parser.rb +272 -0
- data/lib/rgeo/coord_sys/proj4.rb +293 -0
- data/lib/rgeo/coord_sys/srs_database/interface.rb +115 -0
- data/lib/rgeo/coord_sys/srs_database/proj4_data.rb +140 -0
- data/lib/rgeo/coord_sys/srs_database/sr_org.rb +62 -0
- data/lib/rgeo/coord_sys/srs_database/url_reader.rb +63 -0
- data/lib/rgeo/error.rb +27 -0
- data/lib/rgeo/feature.rb +54 -0
- data/lib/rgeo/feature/curve.rb +111 -0
- data/lib/rgeo/feature/factory.rb +278 -0
- data/lib/rgeo/feature/factory_generator.rb +96 -0
- data/lib/rgeo/feature/geometry.rb +624 -0
- data/lib/rgeo/feature/geometry_collection.rb +95 -0
- data/lib/rgeo/feature/line.rb +26 -0
- data/lib/rgeo/feature/line_string.rb +60 -0
- data/lib/rgeo/feature/linear_ring.rb +26 -0
- data/lib/rgeo/feature/mixins.rb +143 -0
- data/lib/rgeo/feature/multi_curve.rb +71 -0
- data/lib/rgeo/feature/multi_line_string.rb +26 -0
- data/lib/rgeo/feature/multi_point.rb +33 -0
- data/lib/rgeo/feature/multi_polygon.rb +57 -0
- data/lib/rgeo/feature/multi_surface.rb +73 -0
- data/lib/rgeo/feature/point.rb +84 -0
- data/lib/rgeo/feature/polygon.rb +97 -0
- data/lib/rgeo/feature/surface.rb +79 -0
- data/lib/rgeo/feature/types.rb +284 -0
- data/lib/rgeo/geographic.rb +40 -0
- data/lib/rgeo/geographic/factory.rb +450 -0
- data/lib/rgeo/geographic/interface.rb +489 -0
- data/lib/rgeo/geographic/proj4_projector.rb +58 -0
- data/lib/rgeo/geographic/projected_feature_classes.rb +107 -0
- data/lib/rgeo/geographic/projected_feature_methods.rb +212 -0
- data/lib/rgeo/geographic/projected_window.rb +383 -0
- data/lib/rgeo/geographic/simple_mercator_projector.rb +110 -0
- data/lib/rgeo/geographic/spherical_feature_classes.rb +100 -0
- data/lib/rgeo/geographic/spherical_feature_methods.rb +134 -0
- data/lib/rgeo/geographic/spherical_math.rb +188 -0
- data/lib/rgeo/geos.rb +89 -0
- data/lib/rgeo/geos/capi_factory.rb +470 -0
- data/lib/rgeo/geos/capi_feature_classes.rb +129 -0
- data/lib/rgeo/geos/ffi_factory.rb +592 -0
- data/lib/rgeo/geos/ffi_feature_classes.rb +83 -0
- data/lib/rgeo/geos/ffi_feature_methods.rb +574 -0
- data/lib/rgeo/geos/interface.rb +202 -0
- data/lib/rgeo/geos/utils.rb +74 -0
- data/lib/rgeo/geos/zm_factory.rb +405 -0
- data/lib/rgeo/geos/zm_feature_classes.rb +80 -0
- data/lib/rgeo/geos/zm_feature_methods.rb +344 -0
- data/lib/rgeo/impl_helper.rb +19 -0
- data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +185 -0
- data/lib/rgeo/impl_helper/basic_geometry_methods.rb +61 -0
- data/lib/rgeo/impl_helper/basic_line_string_methods.rb +146 -0
- data/lib/rgeo/impl_helper/basic_point_methods.rb +104 -0
- data/lib/rgeo/impl_helper/basic_polygon_methods.rb +87 -0
- data/lib/rgeo/impl_helper/math.rb +14 -0
- data/lib/rgeo/impl_helper/utils.rb +29 -0
- data/lib/rgeo/version.rb +3 -0
- data/lib/rgeo/wkrep.rb +37 -0
- data/lib/rgeo/wkrep/wkb_generator.rb +201 -0
- data/lib/rgeo/wkrep/wkb_parser.rb +251 -0
- data/lib/rgeo/wkrep/wkt_generator.rb +207 -0
- data/lib/rgeo/wkrep/wkt_parser.rb +415 -0
- data/lib/rgeo/yaml.rb +23 -0
- data/test/cartesian_analysis_test.rb +65 -0
- data/test/cartesian_bbox_test.rb +123 -0
- data/test/common/factory_tests.rb +78 -0
- data/test/common/geometry_collection_tests.rb +237 -0
- data/test/common/line_string_tests.rb +330 -0
- data/test/common/multi_line_string_tests.rb +182 -0
- data/test/common/multi_point_tests.rb +200 -0
- data/test/common/multi_polygon_tests.rb +191 -0
- data/test/common/point_tests.rb +370 -0
- data/test/common/polygon_tests.rb +261 -0
- data/test/coord_sys/ogc_cs_test.rb +342 -0
- data/test/coord_sys/proj4_srs_data_test.rb +41 -0
- data/test/coord_sys/proj4_test.rb +150 -0
- data/test/coord_sys/sr_org_test.rb +32 -0
- data/test/coord_sys/url_reader_test.rb +42 -0
- data/test/geos_capi/factory_test.rb +31 -0
- data/test/geos_capi/geometry_collection_test.rb +24 -0
- data/test/geos_capi/line_string_test.rb +24 -0
- data/test/geos_capi/misc_test.rb +116 -0
- data/test/geos_capi/multi_line_string_test.rb +24 -0
- data/test/geos_capi/multi_point_test.rb +24 -0
- data/test/geos_capi/multi_polygon_test.rb +39 -0
- data/test/geos_capi/parsing_unparsing_test.rb +40 -0
- data/test/geos_capi/point_test.rb +72 -0
- data/test/geos_capi/polygon_test.rb +154 -0
- data/test/geos_capi/zmfactory_test.rb +57 -0
- data/test/geos_ffi/factory_test.rb +31 -0
- data/test/geos_ffi/geometry_collection_test.rb +24 -0
- data/test/geos_ffi/line_string_test.rb +24 -0
- data/test/geos_ffi/misc_test.rb +63 -0
- data/test/geos_ffi/multi_line_string_test.rb +24 -0
- data/test/geos_ffi/multi_point_test.rb +24 -0
- data/test/geos_ffi/multi_polygon_test.rb +33 -0
- data/test/geos_ffi/parsing_unparsing_test.rb +41 -0
- data/test/geos_ffi/point_test.rb +77 -0
- data/test/geos_ffi/polygon_test.rb +46 -0
- data/test/geos_ffi/zmfactory_test.rb +58 -0
- data/test/mixins_test.rb +141 -0
- data/test/oneoff_test.rb +26 -0
- data/test/projected_geographic/factory_test.rb +25 -0
- data/test/projected_geographic/geometry_collection_test.rb +24 -0
- data/test/projected_geographic/line_string_test.rb +24 -0
- data/test/projected_geographic/multi_line_string_test.rb +26 -0
- data/test/projected_geographic/multi_point_test.rb +30 -0
- data/test/projected_geographic/multi_polygon_test.rb +25 -0
- data/test/projected_geographic/point_test.rb +51 -0
- data/test/projected_geographic/polygon_test.rb +24 -0
- data/test/simple_cartesian/calculations_test.rb +99 -0
- data/test/simple_cartesian/factory_test.rb +27 -0
- data/test/simple_cartesian/geometry_collection_test.rb +30 -0
- data/test/simple_cartesian/line_string_test.rb +31 -0
- data/test/simple_cartesian/multi_line_string_test.rb +28 -0
- data/test/simple_cartesian/multi_point_test.rb +31 -0
- data/test/simple_cartesian/multi_polygon_test.rb +31 -0
- data/test/simple_cartesian/point_test.rb +50 -0
- data/test/simple_cartesian/polygon_test.rb +28 -0
- data/test/simple_mercator/factory_test.rb +25 -0
- data/test/simple_mercator/geometry_collection_test.rb +24 -0
- data/test/simple_mercator/line_string_test.rb +24 -0
- data/test/simple_mercator/multi_line_string_test.rb +26 -0
- data/test/simple_mercator/multi_point_test.rb +29 -0
- data/test/simple_mercator/multi_polygon_test.rb +25 -0
- data/test/simple_mercator/point_test.rb +55 -0
- data/test/simple_mercator/polygon_test.rb +24 -0
- data/test/simple_mercator/window_test.rb +173 -0
- data/test/spherical_geographic/calculations_test.rb +167 -0
- data/test/spherical_geographic/factory_test.rb +27 -0
- data/test/spherical_geographic/geometry_collection_test.rb +31 -0
- data/test/spherical_geographic/line_string_test.rb +31 -0
- data/test/spherical_geographic/multi_line_string_test.rb +29 -0
- data/test/spherical_geographic/multi_point_test.rb +31 -0
- data/test/spherical_geographic/multi_polygon_test.rb +31 -0
- data/test/spherical_geographic/point_test.rb +78 -0
- data/test/spherical_geographic/polygon_test.rb +28 -0
- data/test/types_test.rb +42 -0
- data/test/wkrep/wkb_generator_test.rb +185 -0
- data/test/wkrep/wkb_parser_test.rb +293 -0
- data/test/wkrep/wkt_generator_test.rb +294 -0
- data/test/wkrep/wkt_parser_test.rb +412 -0
- metadata +386 -0
data/lib/rgeo.rb
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# RGeo is a spatial data library for Ruby. It focuses on the storage and
|
|
2
|
+
# manipulation of spatial data types such as points, lines, and polygons.
|
|
3
|
+
#
|
|
4
|
+
# RGeo comprises a number of modules. The "rgeo" gem provides a suite of
|
|
5
|
+
# standard modules. Additional optional modules are provided by separate
|
|
6
|
+
# gems with names of the form "<tt>rgeo-*</tt>".
|
|
7
|
+
#
|
|
8
|
+
# === Standard modules
|
|
9
|
+
#
|
|
10
|
+
# These are the standard modules provided by the "rgeo" gem.
|
|
11
|
+
#
|
|
12
|
+
# * RGeo::Feature contains interface specifications for spatial
|
|
13
|
+
# objects implemented by RGeo. These interfaces closely follow the OGC
|
|
14
|
+
# Simple Features Specifiation (SFS). This module forms the core of RGeo.
|
|
15
|
+
#
|
|
16
|
+
# * RGeo::CoordSys contains classes for representing spatial
|
|
17
|
+
# reference systems and coordinate transformations. For example, it
|
|
18
|
+
# includes a wrapper for the Proj4 library, supporting many geographic
|
|
19
|
+
# projections.
|
|
20
|
+
#
|
|
21
|
+
# * RGeo::Cartesian is a gateway for geometric data implementations
|
|
22
|
+
# that operate in Caresian (flat) coordinate systems. It also provides a
|
|
23
|
+
# basic pure ruby Cartesian implementation. This implementation does not
|
|
24
|
+
# cover all the geometric analysis operations defined by the SFS, but it
|
|
25
|
+
# does not require an external C library and is often sufficient for
|
|
26
|
+
# basic applications.
|
|
27
|
+
#
|
|
28
|
+
# * RGeo::Geos is another Cartesian implementation that wraps the
|
|
29
|
+
# GEOS library to provide a full, high-performance implementation of
|
|
30
|
+
# Cartesian geometry that includes every operation defined in the SFS.
|
|
31
|
+
# It requires GEOS 3.2 or later.
|
|
32
|
+
#
|
|
33
|
+
# * RGeo::Geographic contains spatial implementations that operate
|
|
34
|
+
# in latitude-longitude coordinates and are well-suited for geographic
|
|
35
|
+
# location based applications. Geographic spatial objects may also be
|
|
36
|
+
# linked to projections.
|
|
37
|
+
#
|
|
38
|
+
# * RGeo::WKRep contains tools for reading and writing spatial
|
|
39
|
+
# data in the OGC Well-Known Text (WKT) and Well-Known Binary (WKB)
|
|
40
|
+
# representations. It also supports common variants such as the PostGIS
|
|
41
|
+
# EWKT and EWKB representations.
|
|
42
|
+
#
|
|
43
|
+
# === Optional Modules
|
|
44
|
+
#
|
|
45
|
+
# Here is a partial list of optional modules available as separate gems.
|
|
46
|
+
#
|
|
47
|
+
# * <b>rgeo-geojson</b> provides the RGeo::GeoJSON module, containing
|
|
48
|
+
# tools for GeoJSON encoding and decoding of spatial objects.
|
|
49
|
+
#
|
|
50
|
+
# * <b>rgeo-shapefile</b> provides the RGeo::Shapefile module, containing
|
|
51
|
+
# tools for reading ESRI shapefiles.
|
|
52
|
+
#
|
|
53
|
+
# * <b>rgeo-activerecord</b> provides the RGeo::ActiveRecord module,
|
|
54
|
+
# containing some ActiveRecord extensions for spatial databases, and a
|
|
55
|
+
# set of common tools for ActiveRecord spatial database adapters.
|
|
56
|
+
#
|
|
57
|
+
# Several ActiveRecord adapters use RGeo. These include:
|
|
58
|
+
#
|
|
59
|
+
# * <b>mysqlspatial</b>, an adapter for MySQL spatial extensions based on
|
|
60
|
+
# the mysql adapter. Available as the activerecord-mysqlspatial-adapter
|
|
61
|
+
# gem. <i>This adapter is deprecated due to bugs in the legacy mysql
|
|
62
|
+
# gem. You should use the mysql2spatial adapter instead.</i>
|
|
63
|
+
#
|
|
64
|
+
# * <b>mysql2spatial</b>, an adapter for MySQL spatial extensions based on
|
|
65
|
+
# the mysql2 adapter. Available as the activerecord-mysql2spatial-adapter
|
|
66
|
+
# gem.
|
|
67
|
+
#
|
|
68
|
+
# * <b>spatialite</b>, an adapter for the SpatiaLite extension to the
|
|
69
|
+
# Sqlite3 database, and based on the sqlite3 adapter. Available as the
|
|
70
|
+
# activerecord-spatialite-adapter gem.
|
|
71
|
+
#
|
|
72
|
+
# * <b>postgis</b>, an adapter for the PostGIS extension to the PostgreSQL
|
|
73
|
+
# database, and based on the postgresql adapter. Available as the
|
|
74
|
+
# activerecord-postgis-adapter gem.
|
|
75
|
+
|
|
76
|
+
module RGeo
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Core modules
|
|
80
|
+
require "rgeo/yaml"
|
|
81
|
+
require "rgeo/version"
|
|
82
|
+
require "rgeo/error"
|
|
83
|
+
require "rgeo/feature"
|
|
84
|
+
require "rgeo/coord_sys"
|
|
85
|
+
require "rgeo/impl_helper"
|
|
86
|
+
require "rgeo/wkrep"
|
|
87
|
+
require "rgeo/geos"
|
|
88
|
+
require "rgeo/cartesian"
|
|
89
|
+
require "rgeo/geographic"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
#
|
|
3
|
+
# Cartesian features for RGeo
|
|
4
|
+
#
|
|
5
|
+
# -----------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
module RGeo
|
|
8
|
+
# The Cartesian module is a gateway to implementations that use the
|
|
9
|
+
# Cartesian (i.e. flat) coordinate system. It provides convenient
|
|
10
|
+
# access to Cartesian factories such as the Geos implementation and
|
|
11
|
+
# the simple Cartesian implementation. It also provides a namespace
|
|
12
|
+
# for Cartesian-specific analysis tools.
|
|
13
|
+
|
|
14
|
+
module Cartesian
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Implementation files.
|
|
19
|
+
require "rgeo/cartesian/calculations"
|
|
20
|
+
require "rgeo/cartesian/feature_methods"
|
|
21
|
+
require "rgeo/cartesian/feature_classes"
|
|
22
|
+
require "rgeo/cartesian/factory"
|
|
23
|
+
require "rgeo/cartesian/interface"
|
|
24
|
+
require "rgeo/cartesian/bounding_box"
|
|
25
|
+
require "rgeo/cartesian/analysis"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
#
|
|
3
|
+
# Cartesian geometric analysis utilities
|
|
4
|
+
#
|
|
5
|
+
# -----------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
module RGeo
|
|
8
|
+
module Cartesian
|
|
9
|
+
# This provides includes some spatial analysis algorithms supporting
|
|
10
|
+
# Cartesian data.
|
|
11
|
+
|
|
12
|
+
module Analysis
|
|
13
|
+
class << self
|
|
14
|
+
# Given a LineString, which must be a ring, determine whether the
|
|
15
|
+
# ring proceeds clockwise or counterclockwise.
|
|
16
|
+
# Returns 1 for counterclockwise, or -1 for clockwise.
|
|
17
|
+
#
|
|
18
|
+
# Returns 0 if the ring is empty.
|
|
19
|
+
# The return value is undefined if the object is not a ring, or
|
|
20
|
+
# is not in a Cartesian coordinate system.
|
|
21
|
+
|
|
22
|
+
def ring_direction(ring_)
|
|
23
|
+
size_ = ring_.num_points - 1
|
|
24
|
+
return 0 if size_ == 0
|
|
25
|
+
|
|
26
|
+
# Extract unit-length segments from the ring.
|
|
27
|
+
segs_ = []
|
|
28
|
+
size_.times do |i_|
|
|
29
|
+
p0_ = ring_.point_n(i_)
|
|
30
|
+
p1_ = ring_.point_n(i_ + 1)
|
|
31
|
+
x_ = p1_.x - p0_.x
|
|
32
|
+
y_ = p1_.y - p0_.y
|
|
33
|
+
r_ = ::Math.sqrt(x_ * x_ + y_ * y_)
|
|
34
|
+
if r_ > 0.0
|
|
35
|
+
segs_ << x_ / r_ << y_ / r_
|
|
36
|
+
else
|
|
37
|
+
size_ -= 1
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
segs_ << segs_[0] << segs_[1]
|
|
41
|
+
|
|
42
|
+
# Extract angles from the segments by subtracting the segments.
|
|
43
|
+
# Note angles are represented as cos/sin pairs so we don't
|
|
44
|
+
# have to calculate any trig functions.
|
|
45
|
+
angs_ = []
|
|
46
|
+
size_.times do |i_|
|
|
47
|
+
x0_, y0_, x1_, y1_ = segs_[i_ * 2, 4]
|
|
48
|
+
angs_ << x0_ * x1_ + y0_ * y1_ << x0_ * y1_ - x1_ * y0_
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Now add the angles and count revolutions.
|
|
52
|
+
# Again, our running sum is represented as a cos/sin pair.
|
|
53
|
+
revolutions_ = 0
|
|
54
|
+
sin_ = 0.0
|
|
55
|
+
cos_ = 1.0
|
|
56
|
+
angs_.each_slice(2) do |(x_, y_)|
|
|
57
|
+
ready_ = y_ > 0.0 && sin_ > 0.0 || y_ < 0.0 && sin_ < 0.0
|
|
58
|
+
if y_ != 0.0
|
|
59
|
+
s_ = sin_ * x_ + cos_ * y_
|
|
60
|
+
c_ = cos_ * x_ - sin_ * y_
|
|
61
|
+
r_ = ::Math.sqrt(s_ * s_ + c_ * c_)
|
|
62
|
+
sin_ = s_ / r_
|
|
63
|
+
cos_ = c_ / r_
|
|
64
|
+
end
|
|
65
|
+
next unless ready_
|
|
66
|
+
if y_ > 0.0 && sin_ <= 0.0
|
|
67
|
+
revolutions_ += 1
|
|
68
|
+
elsif y_ < 0.0 && sin_ >= 0.0
|
|
69
|
+
revolutions_ -= 1
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
revolutions_
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
#
|
|
3
|
+
# Cartesian bounding box
|
|
4
|
+
#
|
|
5
|
+
# -----------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
module RGeo
|
|
8
|
+
module Cartesian
|
|
9
|
+
# This is a bounding box for Cartesian data.
|
|
10
|
+
# The simple cartesian implementation uses this internally to compute
|
|
11
|
+
# envelopes. You may also use it directly to compute and represent
|
|
12
|
+
# bounding boxes.
|
|
13
|
+
#
|
|
14
|
+
# A bounding box is a set of ranges in each dimension: X, Y, as well
|
|
15
|
+
# as Z and M if supported. You can compute a bounding box for one or
|
|
16
|
+
# more geometry objects by creating a new bounding box object, and
|
|
17
|
+
# adding the geometries to it. You may then query it for the bounds,
|
|
18
|
+
# or use it to determine whether it encloses other geometries or
|
|
19
|
+
# bounding boxes.
|
|
20
|
+
|
|
21
|
+
class BoundingBox
|
|
22
|
+
# Create a bounding box given two corner points.
|
|
23
|
+
# The bounding box will be given the factory of the first point.
|
|
24
|
+
# You may also provide the same options available to
|
|
25
|
+
# BoundingBox.new.
|
|
26
|
+
|
|
27
|
+
def self.create_from_points(point1_, point2_, opts_ = {})
|
|
28
|
+
factory_ = point1_.factory
|
|
29
|
+
new(factory_, opts_)._add_geometry(point1_).add(point2_)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Create a bounding box given a geometry to surround.
|
|
33
|
+
# The bounding box will be given the factory of the geometry.
|
|
34
|
+
# You may also provide the same options available to
|
|
35
|
+
# BoundingBox.new.
|
|
36
|
+
|
|
37
|
+
def self.create_from_geometry(geom_, opts_ = {})
|
|
38
|
+
factory_ = geom_.factory
|
|
39
|
+
new(factory_, opts_)._add_geometry(geom_)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Create a new empty bounding box with the given factory.
|
|
43
|
+
#
|
|
44
|
+
# The factory defines the coordinate system for the bounding box,
|
|
45
|
+
# and also defines whether it should track Z and M coordinates.
|
|
46
|
+
# All geometries will be cast to this factory when added to this
|
|
47
|
+
# bounding box, and any generated envelope geometry will have this
|
|
48
|
+
# as its factory.
|
|
49
|
+
#
|
|
50
|
+
# Options include:
|
|
51
|
+
#
|
|
52
|
+
# [<tt>:ignore_z</tt>]
|
|
53
|
+
# If true, ignore z coordinates even if the factory supports them.
|
|
54
|
+
# Default is false.
|
|
55
|
+
# [<tt>:ignore_m</tt>]
|
|
56
|
+
# If true, ignore m coordinates even if the factory supports them.
|
|
57
|
+
# Default is false.
|
|
58
|
+
|
|
59
|
+
def initialize(factory_, opts_ = {})
|
|
60
|
+
@factory = factory_
|
|
61
|
+
if (values_ = opts_[:raw])
|
|
62
|
+
@has_z, @has_m, @min_x, @max_x, @min_y, @max_y, @min_z, @max_z, @min_m, @max_m = values_
|
|
63
|
+
else
|
|
64
|
+
@has_z = !opts_[:ignore_z] && factory_.property(:has_z_coordinate) ? true : false
|
|
65
|
+
@has_m = !opts_[:ignore_m] && factory_.property(:has_m_coordinate) ? true : false
|
|
66
|
+
@min_x = @max_x = @min_y = @max_y = @min_z = @max_z = @min_m = @max_m = nil
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def eql?(rhs_) # :nodoc:
|
|
71
|
+
rhs_.is_a?(BoundingBox) && @factory == rhs_.factory &&
|
|
72
|
+
@min_x == rhs_.min_x && @max_x == rhs_.max_x &&
|
|
73
|
+
@min_y == rhs_.min_y && @max_y == rhs_.max_y &&
|
|
74
|
+
@min_z == rhs_.min_z && @max_z == rhs_.max_z &&
|
|
75
|
+
@min_m == rhs_.min_m && @max_m == rhs_.max_m
|
|
76
|
+
end
|
|
77
|
+
alias_method :==, :eql?
|
|
78
|
+
|
|
79
|
+
# Returns the bounding box's factory.
|
|
80
|
+
|
|
81
|
+
attr_reader :factory
|
|
82
|
+
|
|
83
|
+
# Returns true if this bounding box is still empty.
|
|
84
|
+
|
|
85
|
+
def empty?
|
|
86
|
+
@min_x.nil?
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Returns true if this bounding box is degenerate. That is,
|
|
90
|
+
# it is nonempty but contains only a single point because both
|
|
91
|
+
# the X and Y spans are 0. Infinitesimal boxes are also
|
|
92
|
+
# always degenerate.
|
|
93
|
+
|
|
94
|
+
def infinitesimal?
|
|
95
|
+
@min_x && @min_x == @max_x && @min_y == @max_y
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Returns true if this bounding box is degenerate. That is,
|
|
99
|
+
# it is nonempty but has zero area because either or both
|
|
100
|
+
# of the X or Y spans are 0.
|
|
101
|
+
|
|
102
|
+
def degenerate?
|
|
103
|
+
@min_x && (@min_x == @max_x || @min_y == @max_y)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Returns true if this bounding box tracks Z coordinates.
|
|
107
|
+
|
|
108
|
+
attr_reader :has_z
|
|
109
|
+
|
|
110
|
+
# Returns true if this bounding box tracks M coordinates.
|
|
111
|
+
|
|
112
|
+
attr_reader :has_m
|
|
113
|
+
|
|
114
|
+
# Returns the minimum X, or nil if this bounding box is empty.
|
|
115
|
+
|
|
116
|
+
attr_reader :min_x
|
|
117
|
+
|
|
118
|
+
# Returns the maximum X, or nil if this bounding box is empty.
|
|
119
|
+
|
|
120
|
+
attr_reader :max_x
|
|
121
|
+
|
|
122
|
+
# Returns the midpoint X, or nil if this bounding box is empty.
|
|
123
|
+
|
|
124
|
+
def center_x
|
|
125
|
+
@max_x ? (@max_x + @min_x) * 0.5 : nil
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Returns the X span, or 0 if this bounding box is empty.
|
|
129
|
+
|
|
130
|
+
def x_span
|
|
131
|
+
@max_x ? @max_x - @min_x : 0
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Returns the minimum Y, or nil if this bounding box is empty.
|
|
135
|
+
|
|
136
|
+
attr_reader :min_y
|
|
137
|
+
|
|
138
|
+
# Returns the maximum Y, or nil if this bounding box is empty.
|
|
139
|
+
|
|
140
|
+
attr_reader :max_y
|
|
141
|
+
|
|
142
|
+
# Returns the midpoint Y, or nil if this bounding box is empty.
|
|
143
|
+
|
|
144
|
+
def center_y
|
|
145
|
+
@max_y ? (@max_y + @min_y) * 0.5 : nil
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Returns the Y span, or 0 if this bounding box is empty.
|
|
149
|
+
|
|
150
|
+
def y_span
|
|
151
|
+
@max_y ? @max_y - @min_y : 0
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Returns the minimum Z, or nil if this bounding box is empty.
|
|
155
|
+
|
|
156
|
+
attr_reader :min_z
|
|
157
|
+
|
|
158
|
+
# Returns the maximum Z, or nil if this bounding box is empty.
|
|
159
|
+
|
|
160
|
+
attr_reader :max_z
|
|
161
|
+
|
|
162
|
+
# Returns the midpoint Z, or nil if this bounding box is empty or has no Z.
|
|
163
|
+
|
|
164
|
+
def center_z
|
|
165
|
+
@max_z ? (@max_z + @min_z) * 0.5 : nil
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Returns the Z span, 0 if this bounding box is empty, or nil if it has no Z.
|
|
169
|
+
|
|
170
|
+
def z_span
|
|
171
|
+
@has_z ? (@max_z ? @max_z - @min_z : 0) : nil
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Returns the minimum M, or nil if this bounding box is empty.
|
|
175
|
+
|
|
176
|
+
attr_reader :min_m
|
|
177
|
+
|
|
178
|
+
# Returns the maximum M, or nil if this bounding box is empty.
|
|
179
|
+
|
|
180
|
+
attr_reader :max_m
|
|
181
|
+
|
|
182
|
+
# Returns the midpoint M, or nil if this bounding box is empty or has no M.
|
|
183
|
+
|
|
184
|
+
def center_m
|
|
185
|
+
@max_m ? (@max_m + @min_m) * 0.5 : nil
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Returns the M span, 0 if this bounding box is empty, or nil if it has no M.
|
|
189
|
+
|
|
190
|
+
def m_span
|
|
191
|
+
@has_m ? (@max_m ? @max_m - @min_m : 0) : nil
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Returns a point representing the minimum extent in all dimensions,
|
|
195
|
+
# or nil if this bounding box is empty.
|
|
196
|
+
|
|
197
|
+
def min_point
|
|
198
|
+
if @min_x
|
|
199
|
+
extras_ = []
|
|
200
|
+
extras_ << @min_z if @has_z
|
|
201
|
+
extras_ << @min_m if @has_m
|
|
202
|
+
@factory.point(@min_x, @min_y, *extras_)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Returns a point representing the maximum extent in all dimensions,
|
|
207
|
+
# or nil if this bounding box is empty.
|
|
208
|
+
|
|
209
|
+
def max_point
|
|
210
|
+
if @min_x
|
|
211
|
+
extras_ = []
|
|
212
|
+
extras_ << @max_z if @has_z
|
|
213
|
+
extras_ << @max_m if @has_m
|
|
214
|
+
@factory.point(@max_x, @max_y, *extras_)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Adjusts the extents of this bounding box to encompass the given
|
|
219
|
+
# object, which may be a geometry or another bounding box.
|
|
220
|
+
# Returns self.
|
|
221
|
+
|
|
222
|
+
def add(geometry_)
|
|
223
|
+
case geometry_
|
|
224
|
+
when BoundingBox
|
|
225
|
+
add(geometry_.min_point)
|
|
226
|
+
add(geometry_.max_point)
|
|
227
|
+
when Feature::Geometry
|
|
228
|
+
if geometry_.factory == @factory
|
|
229
|
+
_add_geometry(geometry_)
|
|
230
|
+
else
|
|
231
|
+
_add_geometry(Feature.cast(geometry_, @factory))
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
self
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Converts this bounding box to an envelope, which will be the
|
|
238
|
+
# empty collection (if the bounding box is empty), a point (if the
|
|
239
|
+
# bounding box is not empty but both spans are 0), a line (if only
|
|
240
|
+
# one of the two spans is 0) or a polygon (if neither span is 0).
|
|
241
|
+
|
|
242
|
+
def to_geometry
|
|
243
|
+
if @min_x
|
|
244
|
+
extras_ = []
|
|
245
|
+
extras_ << @min_z if @has_z
|
|
246
|
+
extras_ << @min_m if @has_m
|
|
247
|
+
point_min_ = @factory.point(@min_x, @min_y, *extras_)
|
|
248
|
+
if infinitesimal?
|
|
249
|
+
point_min_
|
|
250
|
+
else
|
|
251
|
+
extras_ = []
|
|
252
|
+
extras_ << @max_z if @has_z
|
|
253
|
+
extras_ << @max_m if @has_m
|
|
254
|
+
point_max_ = @factory.point(@max_x, @max_y, *extras_)
|
|
255
|
+
if degenerate?
|
|
256
|
+
@factory.line(point_min_, point_max_)
|
|
257
|
+
else
|
|
258
|
+
@factory.polygon(@factory.linear_ring([point_min_,
|
|
259
|
+
@factory.point(@max_x, @min_y, *extras_), point_max_,
|
|
260
|
+
@factory.point(@min_x, @max_y, *extras_), point_min_]))
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
else
|
|
264
|
+
@factory.collection([])
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Returns true if this bounding box contains the given object,
|
|
269
|
+
# which may be a geometry or another bounding box.
|
|
270
|
+
#
|
|
271
|
+
# Supports these options:
|
|
272
|
+
#
|
|
273
|
+
# [<tt>:ignore_z</tt>]
|
|
274
|
+
# Ignore the Z coordinate when testing, even if both objects
|
|
275
|
+
# have Z. Default is false.
|
|
276
|
+
# [<tt>:ignore_m</tt>]
|
|
277
|
+
# Ignore the M coordinate when testing, even if both objects
|
|
278
|
+
# have M. Default is false.
|
|
279
|
+
|
|
280
|
+
def contains?(rhs_, opts_ = {})
|
|
281
|
+
if Feature::Geometry === rhs_
|
|
282
|
+
contains?(BoundingBox.new(@factory).add(rhs_))
|
|
283
|
+
elsif rhs_.empty?
|
|
284
|
+
true
|
|
285
|
+
elsif empty?
|
|
286
|
+
false
|
|
287
|
+
elsif @min_x > rhs_.min_x || @max_x < rhs_.max_x || @min_y > rhs_.min_y || @max_y < rhs_.max_y
|
|
288
|
+
false
|
|
289
|
+
elsif @has_m && rhs_.has_m && !opts_[:ignore_m] && (@min_m > rhs_.min_m || @max_m < rhs_.max_m)
|
|
290
|
+
false
|
|
291
|
+
elsif @has_z && rhs_.has_z && !opts_[:ignore_z] && (@min_z > rhs_.min_z || @max_z < rhs_.max_z)
|
|
292
|
+
false
|
|
293
|
+
else
|
|
294
|
+
true
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Returns this bounding box subdivided, as an array of bounding boxes.
|
|
299
|
+
# If this bounding box is empty, returns the empty array.
|
|
300
|
+
# If this bounding box is a point, returns a one-element array
|
|
301
|
+
# containing the current point.
|
|
302
|
+
# If the x or y span is 0, bisects the line.
|
|
303
|
+
# Otherwise, generally returns a 4-1 subdivision in the X-Y plane.
|
|
304
|
+
# Does not subdivide on Z or M.
|
|
305
|
+
#
|
|
306
|
+
# [<tt>:bisect_factor</tt>]
|
|
307
|
+
# An optional floating point value that should be greater than 1.0.
|
|
308
|
+
# If the ratio between the larger span and the smaller span is
|
|
309
|
+
# greater than this factor, the bounding box is divided only in
|
|
310
|
+
# half instead of fourths.
|
|
311
|
+
|
|
312
|
+
def subdivide(opts_ = {})
|
|
313
|
+
return [] if empty?
|
|
314
|
+
if infinitesimal?
|
|
315
|
+
return [
|
|
316
|
+
BoundingBox.new(@factory, raw: [@has_z, @has_m,
|
|
317
|
+
@min_x, @max_x, @min_y, @max_y, @min_z, @max_z, @min_m, @max_m])
|
|
318
|
+
]
|
|
319
|
+
end
|
|
320
|
+
factor_ = opts_[:bisect_factor]
|
|
321
|
+
factor_ ||= 1 if degenerate?
|
|
322
|
+
if factor_
|
|
323
|
+
if x_span > y_span * factor_
|
|
324
|
+
return [
|
|
325
|
+
BoundingBox.new(@factory, raw: [@has_z, @has_m,
|
|
326
|
+
@min_x, center_x, @min_y, @max_y, @min_z, @max_z, @min_m, @max_m]),
|
|
327
|
+
BoundingBox.new(@factory, raw: [@has_z, @has_m,
|
|
328
|
+
center_x, @max_x, @min_y, @max_y, @min_z, @max_z, @min_m, @max_m])
|
|
329
|
+
]
|
|
330
|
+
elsif y_span > x_span * factor_
|
|
331
|
+
return [
|
|
332
|
+
BoundingBox.new(@factory, raw: [@has_z, @has_m,
|
|
333
|
+
@min_x, @max_x, @min_y, center_y, @min_z, @max_z, @min_m, @max_m]),
|
|
334
|
+
BoundingBox.new(@factory, raw: [@has_z, @has_m,
|
|
335
|
+
@min_x, @max_x, center_y, @max_y, @min_z, @max_z, @min_m, @max_m])
|
|
336
|
+
]
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
[
|
|
340
|
+
BoundingBox.new(@factory, raw: [@has_z, @has_m,
|
|
341
|
+
@min_x, center_x, @min_y, center_y, @min_z, @max_z, @min_m, @max_m]),
|
|
342
|
+
BoundingBox.new(@factory, raw: [@has_z, @has_m,
|
|
343
|
+
center_x, @max_x, @min_y, center_y, @min_z, @max_z, @min_m, @max_m]),
|
|
344
|
+
BoundingBox.new(@factory, raw: [@has_z, @has_m,
|
|
345
|
+
@min_x, center_x, center_y, @max_y, @min_z, @max_z, @min_m, @max_m]),
|
|
346
|
+
BoundingBox.new(@factory, raw: [@has_z, @has_m,
|
|
347
|
+
center_x, @max_x, center_y, @max_y, @min_z, @max_z, @min_m, @max_m])
|
|
348
|
+
]
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def _add_geometry(geometry_) # :nodoc:
|
|
352
|
+
case geometry_
|
|
353
|
+
when Feature::Point
|
|
354
|
+
_add_point(geometry_)
|
|
355
|
+
when Feature::LineString
|
|
356
|
+
geometry_.points.each { |p_| _add_point(p_) }
|
|
357
|
+
when Feature::Polygon
|
|
358
|
+
geometry_.exterior_ring.points.each { |p_| _add_point(p_) }
|
|
359
|
+
when Feature::MultiPoint
|
|
360
|
+
geometry_.each { |p_| _add_point(p_) }
|
|
361
|
+
when Feature::MultiLineString
|
|
362
|
+
geometry_.each { |line_| line_.points.each { |p_| _add_point(p_) } }
|
|
363
|
+
when Feature::MultiPolygon
|
|
364
|
+
geometry_.each { |poly_| poly_.exterior_ring.points.each { |p_| _add_point(p_) } }
|
|
365
|
+
when Feature::GeometryCollection
|
|
366
|
+
geometry_.each { |g_| _add_geometry(g_) }
|
|
367
|
+
end
|
|
368
|
+
self
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def _add_point(point_) # :nodoc:
|
|
372
|
+
if @min_x
|
|
373
|
+
x_ = point_.x
|
|
374
|
+
@min_x = x_ if x_ < @min_x
|
|
375
|
+
@max_x = x_ if x_ > @max_x
|
|
376
|
+
y_ = point_.y
|
|
377
|
+
@min_y = y_ if y_ < @min_y
|
|
378
|
+
@max_y = y_ if y_ > @max_y
|
|
379
|
+
if @has_z
|
|
380
|
+
z_ = point_.z
|
|
381
|
+
@min_z = z_ if z_ < @min_z
|
|
382
|
+
@max_z = z_ if z_ > @max_z
|
|
383
|
+
end
|
|
384
|
+
if @has_m
|
|
385
|
+
m_ = point_.m
|
|
386
|
+
@min_m = m_ if m_ < @min_m
|
|
387
|
+
@max_m = m_ if m_ > @max_m
|
|
388
|
+
end
|
|
389
|
+
else
|
|
390
|
+
@min_x = @max_x = point_.x
|
|
391
|
+
@min_y = @max_y = point_.y
|
|
392
|
+
@min_z = @max_z = point_.z if @has_z
|
|
393
|
+
@min_m = @max_m = point_.m if @has_m
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
end
|