postgis_adapter 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/MIT-LICENSE +21 -0
- data/Manifest.txt +36 -0
- data/README.rdoc +311 -0
- data/Rakefile +100 -0
- data/init.rb +1 -0
- data/install.rb +0 -0
- data/lib/postgis_adapter.rb +388 -0
- data/lib/postgis_adapter/acts_as_geom.rb +39 -0
- data/lib/postgis_adapter/common_spatial_adapter.rb +179 -0
- data/lib/postgis_functions.rb +158 -0
- data/lib/postgis_functions/bbox.rb +128 -0
- data/lib/postgis_functions/class.rb +64 -0
- data/lib/postgis_functions/common.rb +438 -0
- data/lib/postgis_functions/linestring.rb +172 -0
- data/lib/postgis_functions/point.rb +89 -0
- data/lib/postgis_functions/polygon.rb +78 -0
- data/postgis_adapter.gemspec +38 -0
- data/rails/init.rb +8 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/acts_as_geom_spec.rb +15 -0
- data/spec/common_spatial_adapter_spec.rb +254 -0
- data/spec/db/database_postgis.yml +4 -0
- data/spec/db/models_postgis.rb +56 -0
- data/spec/db/schema_postgis.rb +86 -0
- data/spec/postgis_adapter_spec.rb +174 -0
- data/spec/postgis_functions/bbox_spec.rb +84 -0
- data/spec/postgis_functions/linestring_spec.rb +219 -0
- data/spec/postgis_functions/point_spec.rb +136 -0
- data/spec/postgis_functions/polygon_spec.rb +146 -0
- data/spec/postgis_functions_spec.rb +51 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +25 -0
- data/uninstall.rb +0 -0
- metadata +121 -0
@@ -0,0 +1,158 @@
|
|
1
|
+
# #
|
2
|
+
#
|
3
|
+
# PostGIS Adapter - http://github.com/nofxx/postgis_adapter
|
4
|
+
#
|
5
|
+
# Hope you enjoy this plugin.
|
6
|
+
#
|
7
|
+
#
|
8
|
+
#
|
9
|
+
# Post any bugs/suggestions to the lighthouse tracker:
|
10
|
+
# http://nofxx.lighthouseapp.com/projects/20712-postgisadapter
|
11
|
+
#
|
12
|
+
#
|
13
|
+
# Some links:
|
14
|
+
#
|
15
|
+
# PostGis Manual - http://postgis.refractions.net/documentation/manual-svn/ch07.html
|
16
|
+
# Earth Spheroid - http://en.wikipedia.org/wiki/Figure_of_the_Earth
|
17
|
+
#
|
18
|
+
#
|
19
|
+
#
|
20
|
+
module PostgisFunctions
|
21
|
+
# EARTH_SPHEROID = "'SPHEROID[\"GRS-80\",6378137,298.257222101]'"
|
22
|
+
|
23
|
+
EARTH_SPHEROID = "'SPHEROID[\"IERS_2003\",6378136.6,298.25642]'"
|
24
|
+
|
25
|
+
def postgis_calculate(operation, subjects, options = nil)
|
26
|
+
subjects = [subjects] unless subjects.respond_to?(:map)
|
27
|
+
return execute_geometrical_calculation(operation, subjects, options)
|
28
|
+
rescue Exception => e
|
29
|
+
raise StandardError, "#{e}"
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
|
36
|
+
# Construct the postgis sql query
|
37
|
+
# TODO: ST_Transform() ?? # Convert between distances. Implement this?
|
38
|
+
#
|
39
|
+
# Area return in square feet
|
40
|
+
# Distance/DWithin/Length/Perimeter — in projected units.
|
41
|
+
# DistanceSphere/Spheroid — in meters.
|
42
|
+
#
|
43
|
+
#
|
44
|
+
def construct_geometric_sql(type,geoms,options)
|
45
|
+
|
46
|
+
tables = geoms.map do |t| {
|
47
|
+
:class => t.class.to_s.downcase.pluralize,
|
48
|
+
:uid => unique_identifier,
|
49
|
+
:id => t[:id] }
|
50
|
+
end
|
51
|
+
|
52
|
+
fields = tables.map { |f| "#{f[:uid]}.geom" } # W1.geom
|
53
|
+
conditions = tables.map { |f| "#{f[:uid]}.id = #{f[:id]}" } # W1.id = 5
|
54
|
+
tables.map! { |f| "#{f[:class]} #{f[:uid]}" } # streets W1
|
55
|
+
|
56
|
+
#
|
57
|
+
# Data => SELECT Func(A,B)
|
58
|
+
# BBox => SELECT (A <=> B)
|
59
|
+
#
|
60
|
+
if type == :bbox
|
61
|
+
opcode = nil
|
62
|
+
s_join = " #{options} "
|
63
|
+
else
|
64
|
+
opcode = type.to_s
|
65
|
+
opcode = "ST_#{opcode}" unless opcode =~ /th3d|pesinter/
|
66
|
+
s_join = ","
|
67
|
+
fields << options if options
|
68
|
+
end
|
69
|
+
|
70
|
+
sql = "SELECT #{opcode}(#{fields.join(s_join)}) "
|
71
|
+
sql << "FROM #{tables.join(",")} " if tables
|
72
|
+
sql << "WHERE #{conditions.join(" AND ")}" if conditions
|
73
|
+
#p sql; sql
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Execute the query and parse the return.
|
78
|
+
# We may receive:
|
79
|
+
#
|
80
|
+
# "t" or "f" for boolean queries
|
81
|
+
# BIGHASH for geometries
|
82
|
+
# HASH for ST_Relate
|
83
|
+
# Rescue a float
|
84
|
+
#
|
85
|
+
def execute_geometrical_calculation(operation, subject, options) #:nodoc:
|
86
|
+
value = connection.select_value(construct_geometric_sql(operation, subject, options))
|
87
|
+
return nil unless value
|
88
|
+
if value =~ /t|f/
|
89
|
+
{"f" => false, "t" => true}[value]
|
90
|
+
else
|
91
|
+
GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(value) rescue value
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Get a unique ID for tables
|
96
|
+
def unique_identifier
|
97
|
+
@u_id ||= "W1"
|
98
|
+
@u_id = @u_id.succ
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# POINT(0 0)
|
105
|
+
# LINESTRING(0 0,1 1,1 2)
|
106
|
+
# POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))
|
107
|
+
# MULTIPOINT(0 0,1 2)
|
108
|
+
# MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4))
|
109
|
+
# MULTIPOLYGON(((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)), ..)
|
110
|
+
# GEOMETRYCOLLECTION(POINT(2 3),LINESTRING((2 3,3 4)))
|
111
|
+
#
|
112
|
+
#Accessors
|
113
|
+
#
|
114
|
+
#ST_Dump
|
115
|
+
#ST_ExteriorRing
|
116
|
+
#ST_GeometryN
|
117
|
+
#ST_GeometryType
|
118
|
+
#ST_InteriorRingN
|
119
|
+
#ST_IsEmpty
|
120
|
+
#ST_IsRing
|
121
|
+
#ST_IsSimple
|
122
|
+
#ST_IsValid
|
123
|
+
#ST_mem_size
|
124
|
+
#ST_M
|
125
|
+
#ST_NumGeometries
|
126
|
+
#ST_NumInteriorRings
|
127
|
+
#ST_PointN
|
128
|
+
#ST_SetSRID
|
129
|
+
#ST_Summary1
|
130
|
+
#ST_X
|
131
|
+
#ST_XMin,ST_XMax
|
132
|
+
#ST_Y
|
133
|
+
#YMin,YMax
|
134
|
+
#ST_Z
|
135
|
+
#ZMin,ZMax
|
136
|
+
|
137
|
+
#OUTPUT
|
138
|
+
|
139
|
+
#ST_AsBinary
|
140
|
+
#ST_AsText
|
141
|
+
#ST_AsEWKB
|
142
|
+
#ST_AsEWKT
|
143
|
+
#ST_AsHEXEWKB
|
144
|
+
#ST_AsGML
|
145
|
+
#ST_AsKML
|
146
|
+
#ST_AsSVG
|
147
|
+
#
|
148
|
+
# def distance_convert(value, unit, from = nil)
|
149
|
+
# factor = case unit
|
150
|
+
# when :km, :kilo then 1
|
151
|
+
# when :miles,:mile then 0.62137119
|
152
|
+
# when :cm, :cent then 0.1
|
153
|
+
# when :nmi, :nmile then 0.5399568
|
154
|
+
# end
|
155
|
+
# factor *= 1e3 if from
|
156
|
+
# value * factor
|
157
|
+
# end #use all commands in lowcase form
|
158
|
+
#opcode = opcode.camelize unless opcode =~ /spher|max|npoints/
|
@@ -0,0 +1,128 @@
|
|
1
|
+
###
|
2
|
+
##
|
3
|
+
#
|
4
|
+
# BBox
|
5
|
+
#
|
6
|
+
#
|
7
|
+
module PostgisFunctions
|
8
|
+
|
9
|
+
#
|
10
|
+
# These operators utilize indexes. They compare geometries by bounding boxes.
|
11
|
+
#
|
12
|
+
# You can use the literal forms or call directly using the 'bbox' method. eg.:
|
13
|
+
#
|
14
|
+
# @point.bbox(">>", @area)
|
15
|
+
# @point.bbox("|&>", @area)
|
16
|
+
#
|
17
|
+
#
|
18
|
+
# Cheatsheet:
|
19
|
+
#
|
20
|
+
# A &< B => A overlaps or is to the left of B
|
21
|
+
# A &> B => A overlaps or is to the right of B
|
22
|
+
# A << B => A is strictly to the left of B
|
23
|
+
# A >> B => A is strictly to the right of B
|
24
|
+
# A &<| B => A overlaps B or is below B
|
25
|
+
# A |&> B => A overlaps or is above B
|
26
|
+
# A <<| B => A strictly below B
|
27
|
+
# A |>> B => A strictly above B
|
28
|
+
# A = B => A bbox same as B bbox
|
29
|
+
# A @ B => A completely contained by B
|
30
|
+
# A ~ B => A completely contains B
|
31
|
+
# A && B => A and B bboxes interact
|
32
|
+
# A ~= B => A and B geometries are binary equal?
|
33
|
+
#
|
34
|
+
def bbox(operator, other)
|
35
|
+
postgis_calculate(:bbox, [self, other], operator)
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# bbox literal method.
|
40
|
+
#
|
41
|
+
def completely_contained_by? other
|
42
|
+
bbox("@", other)
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# bbox literal method.
|
47
|
+
#
|
48
|
+
def completely_contains? other
|
49
|
+
bbox("~", other)
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# bbox literal method.
|
54
|
+
#
|
55
|
+
def overlaps_or_above? other
|
56
|
+
bbox("|&>", other)
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# bbox literal method.
|
61
|
+
#
|
62
|
+
def overlaps_or_below? other
|
63
|
+
bbox("&<|", other)
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# bbox literal method.
|
68
|
+
#
|
69
|
+
def overlaps_or_left_of? other
|
70
|
+
bbox("&<", other)
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# bbox literal method.
|
75
|
+
#
|
76
|
+
def overlaps_or_right_of? other
|
77
|
+
bbox("&>", other)
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# bbox literal method.
|
82
|
+
#
|
83
|
+
def strictly_above? other
|
84
|
+
bbox("|>>", other)
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# bbox literal method.
|
89
|
+
#
|
90
|
+
def strictly_below? other
|
91
|
+
bbox("<<|", other)
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# bbox literal method.
|
96
|
+
#
|
97
|
+
def strictly_left_of? other
|
98
|
+
bbox("<<", other)
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# bbox literal method.
|
103
|
+
#
|
104
|
+
def strictly_right_of? other
|
105
|
+
bbox(">>", other)
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# bbox literal method.
|
110
|
+
#
|
111
|
+
def interacts_with? other
|
112
|
+
bbox("&&", other)
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# bbox literal method.
|
117
|
+
#
|
118
|
+
def binary_equal? other
|
119
|
+
bbox("~=", other)
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# bbox literal method.
|
124
|
+
#
|
125
|
+
def same_as? other
|
126
|
+
bbox("=", other)
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module PostgisFunctions
|
2
|
+
|
3
|
+
|
4
|
+
###
|
5
|
+
##
|
6
|
+
#
|
7
|
+
# Class Methods
|
8
|
+
#
|
9
|
+
# Falling back to AR here.
|
10
|
+
#
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
#
|
14
|
+
# Returns the closest record
|
15
|
+
#
|
16
|
+
def closest_to(p, srid=4326)
|
17
|
+
find(:first, :order => "ST_Distance(geom, GeomFromText('POINT(#{p.x} #{p.y})', #{srid}))" )
|
18
|
+
end
|
19
|
+
|
20
|
+
def close_to(p, srid=4326)
|
21
|
+
find(:all, :order => "ST_Distance(geom, GeomFromText('POINT(#{p.x} #{p.y})', #{srid}))" )
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
#
|
26
|
+
#
|
27
|
+
def by_length sort='asc'
|
28
|
+
find(:all, :order => "ST_length(geom) #{sort}" )
|
29
|
+
end
|
30
|
+
|
31
|
+
def longest
|
32
|
+
find(:first, :order => "ST_length(geom) DESC")
|
33
|
+
end
|
34
|
+
|
35
|
+
def contains(p, srid=4326)
|
36
|
+
find(:all, :conditions => ["ST_Contains(geom, GeomFromText('POINT(#{p.x} #{p.y})', #{srid}))"])
|
37
|
+
end
|
38
|
+
|
39
|
+
def contain(p, srid=4326)
|
40
|
+
find(:first, :conditions => ["ST_Contains(geom, GeomFromText('POINT(#{p.x} #{p.y})', #{srid}))"])
|
41
|
+
end
|
42
|
+
|
43
|
+
def by_area sort='asc'
|
44
|
+
find(:all, :order => "ST_Area(geom) #{sort}" )
|
45
|
+
end
|
46
|
+
|
47
|
+
def by_perimeter sort='asc'
|
48
|
+
find(:all, :order => "ST_Perimeter(geom) #{sort}" )
|
49
|
+
end
|
50
|
+
|
51
|
+
def all_within(other, margin=1)
|
52
|
+
# find(:all, :conditions => "ST_DWithin(geom, ST_GeomFromEWKB(E'#{other.as_ewkt}'), #{margin})")
|
53
|
+
find(:all, :conditions => "ST_DWithin(geom, ST_GeomFromEWKT(E'#{other.as_hex_ewkb}'), #{margin})")
|
54
|
+
end
|
55
|
+
|
56
|
+
def by_boundaries sort='asc'
|
57
|
+
find(:all, :order => "ST_Boundary(geom) #{sort}" )
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,438 @@
|
|
1
|
+
# #
|
2
|
+
#
|
3
|
+
# COMMON GEOMETRICAL FUNCTIONS
|
4
|
+
#
|
5
|
+
# The methods here can be used by all geoms.
|
6
|
+
#
|
7
|
+
|
8
|
+
module PostgisFunctions
|
9
|
+
|
10
|
+
#
|
11
|
+
# True if the given geometries represent the same geometry.
|
12
|
+
# Directionality is ignored.
|
13
|
+
#
|
14
|
+
# Returns TRUE if the given Geometries are "spatially equal".
|
15
|
+
# Use this for a 'better' answer than '='. Note by spatially equal we
|
16
|
+
# mean ST_Within(A,B) = true and ST_Within(B,A) = true and also mean ordering
|
17
|
+
# of points can be different but represent the same geometry structure.
|
18
|
+
# To verify the order of points is consistent, use ST_OrderingEquals
|
19
|
+
# (it must be noted ST_OrderingEquals is a little more stringent than
|
20
|
+
# simply verifying order of points are the same).
|
21
|
+
#
|
22
|
+
# This function will return false if either geometry is invalid even
|
23
|
+
# if they are binary equal.
|
24
|
+
#
|
25
|
+
# Returns Boolean ST_Equals(geometry A, geometry B);
|
26
|
+
#
|
27
|
+
def spatially_equal?(other)
|
28
|
+
postgis_calculate(:equals, [self, other])
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Returns the minimum bounding box for the supplied geometry, as a geometry.
|
33
|
+
# The polygon is defined by the corner points of the bounding box
|
34
|
+
# ((MINX, MINY), (MINX, MAXY), (MAXX, MAXY), (MAXX, MINY), (MINX, MINY)).
|
35
|
+
# PostGIS will add a ZMIN/ZMAX coordinate as well/
|
36
|
+
#
|
37
|
+
# Degenerate cases (vertical lines, points) will return a geometry of
|
38
|
+
# lower dimension than POLYGON, ie. POINT or LINESTRING.
|
39
|
+
#
|
40
|
+
# In PostGIS, the bounding box of a geometry is represented internally using
|
41
|
+
# float4s instead of float8s that are used to store geometries. The bounding
|
42
|
+
# box coordinates are floored, guarenteeing that the geometry is contained
|
43
|
+
# entirely within its bounds. This has the advantage that a geometry's
|
44
|
+
# bounding box is half the size as the minimum bounding rectangle,
|
45
|
+
# which means significantly faster indexes and general performance.
|
46
|
+
# But it also means that the bounding box is NOT the same as the minimum
|
47
|
+
# bounding rectangle that bounds the geome.
|
48
|
+
#
|
49
|
+
# Returns GeometryCollection ST_Envelope(geometry g1);
|
50
|
+
#
|
51
|
+
def envelope
|
52
|
+
postgis_calculate(:envelope, self)
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Computes the geometric center of a geometry, or equivalently,
|
57
|
+
# the center of mass of the geometry as a POINT. For [MULTI]POINTs, this is
|
58
|
+
# computed as the arithmetric mean of the input coordinates.
|
59
|
+
# For [MULTI]LINESTRINGs, this is computed as the weighted length of each
|
60
|
+
# line segment. For [MULTI]POLYGONs, "weight" is thought in terms of area.
|
61
|
+
# If an empty geometry is supplied, an empty GEOMETRYCOLLECTION is returned.
|
62
|
+
# If NULL is supplied, NULL is returned.
|
63
|
+
#
|
64
|
+
# The centroid is equal to the centroid of the set of component Geometries of
|
65
|
+
# highest dimension (since the lower-dimension geometries contribute zero
|
66
|
+
# "weight" to the centroid).
|
67
|
+
#
|
68
|
+
# Computation will be more accurate if performed by the GEOS module (enabled at compile time).
|
69
|
+
#
|
70
|
+
# http://postgis.refractions.net/documentation/manual-svn/ST_Centroid.html
|
71
|
+
#
|
72
|
+
# Returns Geometry ST_Centroid(geometry g1);
|
73
|
+
#
|
74
|
+
def centroid
|
75
|
+
postgis_calculate(:centroid, self)
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# Returns the closure of the combinatorial boundary of this Geometry.
|
80
|
+
# The combinatorial boundary is defined as described in section 3.12.3.2 of the
|
81
|
+
# OGC SPEC. Because the result of this function is a closure, and hence topologically
|
82
|
+
# closed, the resulting boundary can be represented using representational
|
83
|
+
# geometry primitives as discussed in the OGC SPEC, section 3.12.2.
|
84
|
+
#
|
85
|
+
# Do not call with a GEOMETRYCOLLECTION as an argument.
|
86
|
+
#
|
87
|
+
# Performed by the GEOS module.
|
88
|
+
#
|
89
|
+
# Returns Geometry ST_Boundary(geometry geomA);
|
90
|
+
#
|
91
|
+
def boundary
|
92
|
+
postgis_calculate(:boundary, self)
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# 2D minimum cartesian distance between two geometries in projected units.
|
97
|
+
#
|
98
|
+
# Returns Float ST_Distance(geometry g1, geometry g2);
|
99
|
+
#
|
100
|
+
def distance_to(other)
|
101
|
+
postgis_calculate(:distance, [self, other]).to_f
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# True if geometry A is completely inside geometry B.
|
106
|
+
#
|
107
|
+
# For this function to make sense, the source geometries must both be of the same
|
108
|
+
# coordinate projection, having the same SRID. It is a given that
|
109
|
+
# if ST_Within(A,B) is true and ST_Within(B,A) is true, then the
|
110
|
+
# two geometries are considered spatially equal.
|
111
|
+
#
|
112
|
+
# This function call will automatically include a bounding box comparison that will
|
113
|
+
# make use of any indexes that are available on the geometries. To avoid index use,
|
114
|
+
# use the function _ST_Within.
|
115
|
+
#
|
116
|
+
# Do not call with a GEOMETRYCOLLECTION as an argument
|
117
|
+
# Do not use this function with invalid geometries. You will get unexpected results.
|
118
|
+
#
|
119
|
+
# Performed by the GEOS module.
|
120
|
+
#
|
121
|
+
# Returns Boolean ST_Within(geometry A, geometry B);
|
122
|
+
#
|
123
|
+
def within? other
|
124
|
+
postgis_calculate(:within, [self, other])
|
125
|
+
end
|
126
|
+
|
127
|
+
#
|
128
|
+
# True if the geometries are within the specified distance of one another.
|
129
|
+
# The distance is specified in units defined by the spatial reference system
|
130
|
+
# of the geometries. For this function to make sense, the source geometries
|
131
|
+
# must both be of the same coorindate projection, having the same SRID.
|
132
|
+
#
|
133
|
+
# Returns boolean ST_DWithin(geometry g1, geometry g2, double precision distance);
|
134
|
+
#
|
135
|
+
def d_within?(other, margin=0.1)
|
136
|
+
postgis_calculate(:dwithin, [self, other], margin)
|
137
|
+
end
|
138
|
+
alias_method "in_bounds?", "d_within?"
|
139
|
+
|
140
|
+
#
|
141
|
+
# True if geometry B is completely inside geometry A.
|
142
|
+
#
|
143
|
+
# For this function to make sense, the source geometries must both be of the same
|
144
|
+
# coordinate projection, having the same SRID. 'contains?' is the inverse of 'within?'.
|
145
|
+
#
|
146
|
+
# So a.contains?(b) is like b.within?(a) except in the case of invalid
|
147
|
+
# geometries where the result is always false regardless or not defined.
|
148
|
+
#
|
149
|
+
# Do not call with a GEOMETRYCOLLECTION as an argument
|
150
|
+
# Do not use this function with invalid geometries. You will get unexpected results.
|
151
|
+
#
|
152
|
+
# Performed by the GEOS module
|
153
|
+
#
|
154
|
+
# Returns Boolean ST_Contains(geometry geomA, geometry geomB);
|
155
|
+
#
|
156
|
+
def contains? other
|
157
|
+
postgis_calculate(:contains, [self, other])
|
158
|
+
end
|
159
|
+
|
160
|
+
#
|
161
|
+
# True if no point in Geometry A is outside Geometry B
|
162
|
+
#
|
163
|
+
# This function call will automatically include a bounding box comparison that
|
164
|
+
# will make use of any indexes that are available on the geometries. To avoid
|
165
|
+
# index use, use the function _ST_CoveredBy.
|
166
|
+
#
|
167
|
+
# Do not call with a GEOMETRYCOLLECTION as an argument.
|
168
|
+
# Do not use this function with invalid geometries. You will get unexpected results.
|
169
|
+
#
|
170
|
+
# Performed by the GEOS module.
|
171
|
+
#
|
172
|
+
# Aliased as 'inside?'
|
173
|
+
#
|
174
|
+
# Returns Boolean ST_CoveredBy(geometry geomA, geometry geomB);
|
175
|
+
#
|
176
|
+
def covered_by? other
|
177
|
+
postgis_calculate(:coveredby, [self, other])
|
178
|
+
end
|
179
|
+
alias_method "inside?", "covered_by?"
|
180
|
+
|
181
|
+
#
|
182
|
+
# Eye-candy. See 'covered_by?'.
|
183
|
+
#
|
184
|
+
# Returns !(Boolean ST_CoveredBy(geometry geomA, geometry geomB);)
|
185
|
+
#
|
186
|
+
def outside? other
|
187
|
+
!covered_by? other
|
188
|
+
end
|
189
|
+
|
190
|
+
#
|
191
|
+
# True if the Geometries do not "spatially intersect" - if they
|
192
|
+
# do not share any space together.
|
193
|
+
#
|
194
|
+
# Overlaps, Touches, Within all imply geometries are not spatially disjoint.
|
195
|
+
# If any of the aforementioned returns true, then the geometries are not
|
196
|
+
# spatially disjoint. Disjoint implies false for spatial intersection.
|
197
|
+
#
|
198
|
+
# Do not call with a GEOMETRYCOLLECTION as an argument.
|
199
|
+
#
|
200
|
+
# Returns boolean ST_Disjoint( geometry A , geometry B );
|
201
|
+
#
|
202
|
+
def disjoint? other
|
203
|
+
postgis_calculate(:disjoint, [self, other])
|
204
|
+
end
|
205
|
+
|
206
|
+
#
|
207
|
+
# How many dimensions the geom is made of (2, 3 or 4)
|
208
|
+
#
|
209
|
+
# Returns Integer ST_Dimension(geom g1)
|
210
|
+
#
|
211
|
+
def dimension
|
212
|
+
postgis_calculate(:dimension, self).to_i
|
213
|
+
end
|
214
|
+
|
215
|
+
#
|
216
|
+
# Returns a "simplified" version of the given geometry using the Douglas-Peuker
|
217
|
+
# algorithm. Will actually do something only with (multi)lines and (multi)polygons
|
218
|
+
# but you can safely call it with any kind of geometry. Since simplification
|
219
|
+
# occurs on a object-by-object basis you can also feed a GeometryCollection to this
|
220
|
+
# function.
|
221
|
+
#
|
222
|
+
# Note that returned geometry might loose its simplicity (see 'is_simple?').
|
223
|
+
# Topology may not be preserved and may result in invalid geometries.
|
224
|
+
# Use 'simplify_preserve_topology' to preserve topology.
|
225
|
+
#
|
226
|
+
# Performed by the GEOS Module.
|
227
|
+
#
|
228
|
+
# Returns Geometry ST_Simplify(geometry geomA, float tolerance);
|
229
|
+
#
|
230
|
+
def simplify(tolerance=0.1)
|
231
|
+
postgis_calculate(:simplify, self, tolerance)
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
def simplify!(tolerance=0.1)
|
236
|
+
#FIXME: not good..
|
237
|
+
self.geom = simplify
|
238
|
+
end
|
239
|
+
|
240
|
+
#
|
241
|
+
# Returns a "simplified" version of the given geometry using the Douglas-Peuker
|
242
|
+
# algorithm. Will avoid creating derived geometries (polygons in particular) that
|
243
|
+
# are invalid. Will actually do something only with (multi)lines and (multi)polygons
|
244
|
+
# but you can safely call it with any kind of geometry. Since simplification occurs
|
245
|
+
# on a object-by-object basis you can also feed a GeometryCollection to this function.
|
246
|
+
#
|
247
|
+
# Performed by the GEOS module. Requires GEOS 3.0.0+
|
248
|
+
#
|
249
|
+
# Returns Geometry ST_SimplifyPreserveTopology(geometry geomA, float tolerance);
|
250
|
+
#
|
251
|
+
def simplify_preserve_topology(tolerance=0.1)
|
252
|
+
postgis_calculate(:simplifypreservetopology, self, tolerance)
|
253
|
+
end
|
254
|
+
|
255
|
+
#
|
256
|
+
# True if Geometries "spatially intersect", share any portion of space.
|
257
|
+
# False if they don't (they are Disjoint).
|
258
|
+
#
|
259
|
+
# 'overlaps?', 'touches?', 'within?' all imply spatial intersection.
|
260
|
+
# If any of the aforementioned returns true, then the geometries also
|
261
|
+
# spatially intersect. 'disjoint?' implies false for spatial intersection.
|
262
|
+
#
|
263
|
+
# Returns Boolean ST_Intersects(geometry geomA, geometry geomB);
|
264
|
+
#
|
265
|
+
def intersects? other
|
266
|
+
postgis_calculate(:intersects, [self, other])
|
267
|
+
end
|
268
|
+
|
269
|
+
#
|
270
|
+
# True if a Geometry`s Envelope "spatially intersect", share any portion of space.
|
271
|
+
#
|
272
|
+
# It`s 'intersects?', for envelopes.
|
273
|
+
#
|
274
|
+
# Returns Boolean SE_EnvelopesIntersect(geometry geomA, geometry geomB);
|
275
|
+
#
|
276
|
+
def envelopes_intersect? other
|
277
|
+
postgis_calculate(:se_envelopesintersect, [self, other])
|
278
|
+
end
|
279
|
+
|
280
|
+
#
|
281
|
+
# Geometry that represents the point set intersection of the Geometries.
|
282
|
+
# In other words - that portion of geometry A and geometry B that is shared between
|
283
|
+
# the two geometries. If the geometries do not share any space (are disjoint),
|
284
|
+
# then an empty geometry collection is returned.
|
285
|
+
#
|
286
|
+
# 'intersection' in conjunction with intersects? is very useful for clipping
|
287
|
+
# geometries such as in bounding box, buffer, region queries where you only want
|
288
|
+
# to return that portion of a geometry that sits in a country or region of interest.
|
289
|
+
#
|
290
|
+
# Do not call with a GEOMETRYCOLLECTION as an argument.
|
291
|
+
# Performed by the GEOS module.
|
292
|
+
#
|
293
|
+
# Returns Geometry ST_Intersection(geometry geomA, geometry geomB);
|
294
|
+
#
|
295
|
+
def intersection other
|
296
|
+
postgis_calculate(:intersection, [self, other])
|
297
|
+
end
|
298
|
+
|
299
|
+
#
|
300
|
+
# True if the Geometries share space, are of the same dimension, but are
|
301
|
+
# not completely contained by each other. They intersect, but one does not
|
302
|
+
# completely contain another.
|
303
|
+
#
|
304
|
+
# Do not call with a GeometryCollection as an argument
|
305
|
+
# This function call will automatically include a bounding box comparison that
|
306
|
+
# will make use of any indexes that are available on the geometries. To avoid
|
307
|
+
# index use, use the function _ST_Overlaps.
|
308
|
+
#
|
309
|
+
# Performed by the GEOS module.
|
310
|
+
#
|
311
|
+
# Returns Boolean ST_Overlaps(geometry A, geometry B);
|
312
|
+
#
|
313
|
+
def overlaps? other
|
314
|
+
postgis_calculate(:overlaps, [self, other])
|
315
|
+
end
|
316
|
+
|
317
|
+
# True if the geometries have at least one point in common,
|
318
|
+
# but their interiors do not intersect.
|
319
|
+
#
|
320
|
+
# If the only points in common between g1 and g2 lie in the union of the
|
321
|
+
# boundaries of g1 and g2. The 'touches?' relation applies to all Area/Area,
|
322
|
+
# Line/Line, Line/Area, Point/Area and Point/Line pairs of relationships,
|
323
|
+
# but not to the Point/Point pair.
|
324
|
+
#
|
325
|
+
# Returns Boolean ST_Touches(geometry g1, geometry g2);
|
326
|
+
#
|
327
|
+
def touches? other
|
328
|
+
postgis_calculate(:touches, [self, other])
|
329
|
+
end
|
330
|
+
|
331
|
+
#
|
332
|
+
# The convex hull of a geometry represents the minimum closed geometry that
|
333
|
+
# encloses all geometries within the set.
|
334
|
+
#
|
335
|
+
# It is usually used with MULTI and Geometry Collections. Although it is not
|
336
|
+
# an aggregate - you can use it in conjunction with ST_Collect to get the convex
|
337
|
+
# hull of a set of points. ST_ConvexHull(ST_Collect(somepointfield)).
|
338
|
+
# It is often used to determine an affected area based on a set of point observations.
|
339
|
+
#
|
340
|
+
# Performed by the GEOS module.
|
341
|
+
#
|
342
|
+
# Returns Geometry ST_ConvexHull(geometry geomA);
|
343
|
+
#
|
344
|
+
def convex_hull
|
345
|
+
postgis_calculate(:convexhull, self)
|
346
|
+
end
|
347
|
+
|
348
|
+
#
|
349
|
+
# Creates an areal geometry formed by the constituent linework of given geometry.
|
350
|
+
# The return type can be a Polygon or MultiPolygon, depending on input.
|
351
|
+
# If the input lineworks do not form polygons NULL is returned. The inputs can
|
352
|
+
# be LINESTRINGS, MULTILINESTRINGS, POLYGONS, MULTIPOLYGONS, and GeometryCollections.
|
353
|
+
#
|
354
|
+
# Returns Boolean ST_BuildArea(geometry A);
|
355
|
+
#
|
356
|
+
def build_area
|
357
|
+
postgis_calculate(:buildarea, self)
|
358
|
+
end
|
359
|
+
|
360
|
+
#
|
361
|
+
# Returns true if this Geometry has no anomalous geometric points, such as
|
362
|
+
# self intersection or self tangency.
|
363
|
+
#
|
364
|
+
# Returns boolean ST_IsSimple(geometry geomA);
|
365
|
+
#
|
366
|
+
def is_simple?
|
367
|
+
postgis_calculate(:issimple, self)
|
368
|
+
end
|
369
|
+
alias_method "simple?", "is_simple?"
|
370
|
+
|
371
|
+
#
|
372
|
+
# Aggregate. Creates a GeometryCollection containing possible polygons formed
|
373
|
+
# from the constituent linework of a set of geometries.
|
374
|
+
#
|
375
|
+
# Geometry Collections are often difficult to deal with with third party tools,
|
376
|
+
# so use ST_Polygonize in conjunction with ST_Dump to dump the polygons out into
|
377
|
+
# individual polygons.
|
378
|
+
#
|
379
|
+
# Returns Geometry ST_Polygonize(geometry set geomfield);
|
380
|
+
#
|
381
|
+
def polygonize#(geom)
|
382
|
+
postgis_calculate(:polygonize, self)
|
383
|
+
end
|
384
|
+
|
385
|
+
#
|
386
|
+
# Returns true if this Geometry is spatially related to anotherGeometry,
|
387
|
+
# by testing for intersections between the Interior, Boundary and Exterior
|
388
|
+
# of the two geometries as specified by the values in the
|
389
|
+
# intersectionPatternMatrix. If no intersectionPatternMatrix is passed in,
|
390
|
+
# then returns the maximum intersectionPatternMatrix that relates the 2 geometries.
|
391
|
+
#
|
392
|
+
#
|
393
|
+
# Version 1: Takes geomA, geomB, intersectionMatrix and Returns 1 (TRUE) if
|
394
|
+
# this Geometry is spatially related to anotherGeometry, by testing for
|
395
|
+
# intersections between the Interior, Boundary and Exterior of the two
|
396
|
+
# geometries as specified by the values in the intersectionPatternMatrix.
|
397
|
+
#
|
398
|
+
# This is especially useful for testing compound checks of intersection,
|
399
|
+
# crosses, etc in one step.
|
400
|
+
#
|
401
|
+
# Do not call with a GeometryCollection as an argument
|
402
|
+
#
|
403
|
+
# This is the "allowable" version that returns a boolean, not an integer.
|
404
|
+
# This is defined in OGC spec.
|
405
|
+
# This DOES NOT automagically include an index call. The reason for that
|
406
|
+
# is some relationships are anti e.g. Disjoint. If you are using a relationship
|
407
|
+
# pattern that requires intersection, then include the && index call.
|
408
|
+
#
|
409
|
+
# Version 2: Takes geomA and geomB and returns the DE-9IM
|
410
|
+
# (dimensionally extended nine-intersection matrix)
|
411
|
+
#
|
412
|
+
# Do not call with a GeometryCollection as an argument
|
413
|
+
# Not in OGC spec, but implied. see s2.1.13.2
|
414
|
+
#
|
415
|
+
# Both Performed by the GEOS module
|
416
|
+
#
|
417
|
+
# Returns:
|
418
|
+
#
|
419
|
+
# text ST_Relate(geometry geomA, geometry geomB);
|
420
|
+
# boolean ST_Relate(geometry geomA, geometry geomB, text intersectionPatternMatrix);
|
421
|
+
#
|
422
|
+
def relate?(other, m = nil)
|
423
|
+
# Relate is case sentitive.......
|
424
|
+
m = "'#{m}'" if m
|
425
|
+
postgis_calculate("Relate", [self, other], m)
|
426
|
+
end
|
427
|
+
|
428
|
+
end
|
429
|
+
|
430
|
+
# NEW
|
431
|
+
#ST_OrderingEquals — Returns true if the given geometries represent the same geometry and points are in the same directional order.
|
432
|
+
#boolean ST_OrderingEquals(g
|
433
|
+
# ST_PointOnSurface — Returns a POINT guaranteed to lie on the surface.
|
434
|
+
#geometry ST_PointOnSurface(geometry g1);eometry A, geometry B);
|
435
|
+
|
436
|
+
|
437
|
+
#x ST_SnapToGrid(geometry, geometry, sizeX, sizeY, sizeZ, sizeM)
|
438
|
+
# ST_X , ST_Y, SE_M, SE_Z, SE_IsMeasured has_m?
|