postgis_adapter 0.2.1 → 0.2.3
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.
- data/README.rdoc +48 -33
- data/lib/postgis_adapter.rb +5 -3
- data/lib/postgis_functions/bbox.rb +2 -2
- data/lib/postgis_functions/common.rb +82 -68
- data/lib/postgis_functions.rb +24 -18
- data/postgis_adapter.gemspec +2 -2
- data/spec/postgis_functions/bbox_spec.rb +41 -47
- data/spec/postgis_functions/common_spec.rb +28 -12
- data/spec/postgis_functions_spec.rb +16 -6
- data/spec/spec.opts +1 -1
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -11,32 +11,42 @@ Postgis Manual - http://postgis.refractions.net/documentation/manual-svn
|
|
11
11
|
*PostGIS and Rails 2+ only*.
|
12
12
|
|
13
13
|
|
14
|
-
|
14
|
+
== Install
|
15
15
|
|
16
16
|
If you are using Spatial Adapter, *remove it first*.
|
17
17
|
|
18
18
|
|
19
|
-
|
19
|
+
=== Dependencies
|
20
20
|
|
21
21
|
- georuby
|
22
22
|
- postgres 8.3+
|
23
23
|
- postgis 1.3+
|
24
24
|
|
25
25
|
|
26
|
-
|
26
|
+
=== As gem:
|
27
|
+
|
28
|
+
sudo gem install postgis-adapter
|
29
|
+
|
30
|
+
Rails:
|
31
|
+
|
32
|
+
config.gem "postgis_adapter"
|
33
|
+
|
34
|
+
Github version:
|
35
|
+
|
36
|
+
|
37
|
+
sudo gem install nofxx-postgis_adapter --source http://gems.github.com
|
38
|
+
|
39
|
+
Rails:
|
27
40
|
|
28
|
-
sudo gem sources --add http://gems.github.com
|
29
|
-
sudo gem install nofxx-postgis_adapter
|
30
|
-
|
31
41
|
config.gem "nofxx-postgis_adapter", :lib => "postgis_adapter", :source => "http://gems.github.com"
|
32
|
-
|
33
|
-
|
34
|
-
|
42
|
+
|
43
|
+
|
44
|
+
=== As plugin:
|
35
45
|
|
36
46
|
script/plugin install git://github.com/nofxx/postgis_adapter.git
|
37
47
|
|
38
48
|
|
39
|
-
|
49
|
+
== How to Use
|
40
50
|
|
41
51
|
Geometric columns in your ActiveRecord models now appear just like
|
42
52
|
any other column of other basic data types. They can also be dumped
|
@@ -55,11 +65,11 @@ The plugin will get this information by itself.
|
|
55
65
|
Here is an example of PostGIS row creation and access, using the
|
56
66
|
model and the table defined above :
|
57
67
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
68
|
+
pt = TablePoint.new(:data => "Hello!",:geom => Point.from_x_y(1,2))
|
69
|
+
pt.save
|
70
|
+
pt = TablePoint.first
|
71
|
+
puts pt.geom.x
|
72
|
+
=> 1
|
63
73
|
|
64
74
|
|
65
75
|
== PostGIS Functions
|
@@ -78,7 +88,7 @@ Here are this fork additions. To use it:
|
|
78
88
|
@street = Street.new( :line => **LineString** )
|
79
89
|
|
80
90
|
|
81
|
-
|
91
|
+
== Play!
|
82
92
|
|
83
93
|
@place.inside?(@park)
|
84
94
|
=> true
|
@@ -91,9 +101,9 @@ Here are this fork additions. To use it:
|
|
91
101
|
@street.crosses?(@park)
|
92
102
|
|
93
103
|
@area.contains?(@place)
|
94
|
-
|
95
104
|
|
96
|
-
|
105
|
+
|
106
|
+
=== Polygons:
|
97
107
|
|
98
108
|
@park.area
|
99
109
|
=> 1345
|
@@ -103,9 +113,14 @@ Polygons:
|
|
103
113
|
|
104
114
|
@park.overlaps?(@other_park)
|
105
115
|
=> false
|
106
|
-
|
107
116
|
|
108
|
-
|
117
|
+
Supports transform (useful to transform SRID to UTM for area in Km^2)
|
118
|
+
|
119
|
+
@park.area(SRID)
|
120
|
+
=> Area with new SRID
|
121
|
+
|
122
|
+
|
123
|
+
=== Line Strings:
|
109
124
|
|
110
125
|
@street_east.intersects?(@street_west)
|
111
126
|
=> false
|
@@ -120,7 +135,7 @@ Line Strings:
|
|
120
135
|
=> 4.40853636
|
121
136
|
|
122
137
|
|
123
|
-
===
|
138
|
+
=== Class Methods
|
124
139
|
|
125
140
|
City.close_to(@point)
|
126
141
|
=> [Array of cities in order by distance...
|
@@ -158,7 +173,7 @@ Line Strings:
|
|
158
173
|
same_as?
|
159
174
|
|
160
175
|
|
161
|
-
Or use a (almost)
|
176
|
+
Or use a (almost) PostGIS like notation:
|
162
177
|
|
163
178
|
@area.bbox "<<", @point
|
164
179
|
|
@@ -200,15 +215,15 @@ the find_by_*geom_column*: Either by passing a geometric object directly,
|
|
200
215
|
or passing an array with the 2 opposite corners of a bounding box
|
201
216
|
(with 2 or 3 coordinates depending of the dimension of the data).
|
202
217
|
|
203
|
-
|
218
|
+
Park.find_by_geom(LineString.from_coordinates([[1.4,5.6],[2.7,8.9],[1.6,5.6]]))
|
204
219
|
|
205
220
|
or
|
206
221
|
|
207
|
-
|
222
|
+
Park.find_by_geom([[3,5.6],[19.98,5.9]])
|
208
223
|
|
209
224
|
In PostGIS, since you can only use operations with geometries with the same SRID, you can add a third element representing the SRID of the bounding box to the array. It is by default set to -1:
|
210
225
|
|
211
|
-
|
226
|
+
Park.find_by_geom([[3,5.6],[19.98,5.9],123])
|
212
227
|
|
213
228
|
|
214
229
|
|
@@ -221,13 +236,13 @@ geometric column in PostGIS, along with the addition of a spatial
|
|
221
236
|
index on the column :
|
222
237
|
|
223
238
|
ActiveRecord::Schema.define do
|
224
|
-
|
239
|
+
create_table :places do |t|
|
225
240
|
t.string :name
|
226
|
-
|
227
|
-
|
241
|
+
t.point :geom, :srid => 123, :with_z => true, :null => false
|
242
|
+
|
228
243
|
t.timestamps
|
229
244
|
end
|
230
|
-
|
245
|
+
add_index :table_points, :geom, :spatial=>true
|
231
246
|
end
|
232
247
|
|
233
248
|
|
@@ -242,9 +257,9 @@ is different for each database). You would use it like this, if
|
|
242
257
|
the geometric column is a point:
|
243
258
|
|
244
259
|
fixture:
|
245
|
-
|
246
|
-
|
247
|
-
|
260
|
+
id: 1
|
261
|
+
data: HELLO
|
262
|
+
geom: <%= Point.from_x_y(123.5,321.9).to_yaml %>
|
248
263
|
|
249
264
|
|
250
265
|
=== Annotate
|
@@ -295,7 +310,7 @@ Postgis Adapter is released under the MIT license.
|
|
295
310
|
|
296
311
|
== Support
|
297
312
|
|
298
|
-
Tested using rails 2.2.2 / postgresql 8.3.5 / postgis 1.3.3 / linux / osx
|
313
|
+
Tested using rails 2.2.2/2.3.0 / postgresql 8.3.5 / postgis 1.3.3 / linux / osx
|
299
314
|
|
300
315
|
Any questions, enhancement proposals, bug notifications or corrections:
|
301
316
|
|
data/lib/postgis_adapter.rb
CHANGED
@@ -4,7 +4,8 @@
|
|
4
4
|
# Spatial Adapter PostGIS Adapter for ActiveRecord
|
5
5
|
#
|
6
6
|
#
|
7
|
-
|
7
|
+
require 'activerecord'
|
8
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
8
9
|
require 'geo_ruby'
|
9
10
|
require 'postgis_adapter/common_spatial_adapter'
|
10
11
|
require 'postgis_functions'
|
@@ -17,7 +18,7 @@ include GeoRuby::SimpleFeatures
|
|
17
18
|
include SpatialAdapter
|
18
19
|
|
19
20
|
module PostgisAdapter
|
20
|
-
VERSION = '0.2.
|
21
|
+
VERSION = '0.2.3'
|
21
22
|
end
|
22
23
|
|
23
24
|
#tables to ignore in migration : relative to PostGIS management of geometric columns
|
@@ -250,7 +251,8 @@ SELECT * FROM geometry_columns WHERE f_table_name = '#{table_name}'
|
|
250
251
|
end
|
251
252
|
|
252
253
|
raw_geom_infos
|
253
|
-
|
254
|
+
rescue => e
|
255
|
+
nil
|
254
256
|
end
|
255
257
|
|
256
258
|
end
|
@@ -10,7 +10,7 @@ module PostgisFunctions
|
|
10
10
|
# These operators utilize indexes. They compare geometries by bounding boxes.
|
11
11
|
#
|
12
12
|
# You can use the literal forms or call directly using the 'bbox' method. eg.:
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# @point.bbox(">>", @area)
|
15
15
|
# @point.bbox("|&>", @area)
|
16
16
|
#
|
@@ -125,4 +125,4 @@ module PostgisFunctions
|
|
125
125
|
def same_as? other
|
126
126
|
bbox("=", other)
|
127
127
|
end
|
128
|
-
end
|
128
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
# #
|
2
3
|
#
|
3
4
|
# COMMON GEOMETRICAL FUNCTIONS
|
@@ -19,7 +20,7 @@ module PostgisFunctions
|
|
19
20
|
# (it must be noted ST_OrderingEquals is a little more stringent than
|
20
21
|
# simply verifying order of points are the same).
|
21
22
|
#
|
22
|
-
# This function will return false if either geometry is invalid even
|
23
|
+
# This function will return false if either geometry is invalid even
|
23
24
|
# if they are binary equal.
|
24
25
|
#
|
25
26
|
# Returns Boolean ST_Equals(geometry A, geometry B);
|
@@ -74,9 +75,9 @@ module PostgisFunctions
|
|
74
75
|
def centroid
|
75
76
|
postgis_calculate(:centroid, self)
|
76
77
|
end
|
77
|
-
|
78
|
+
|
78
79
|
#
|
79
|
-
# Returns the closure of the combinatorial boundary of this Geometry.
|
80
|
+
# Returns the closure of the combinatorial boundary of this Geometry.
|
80
81
|
# The combinatorial boundary is defined as described in section 3.12.3.2 of the
|
81
82
|
# OGC SPEC. Because the result of this function is a closure, and hence topologically
|
82
83
|
# closed, the resulting boundary can be represented using representational
|
@@ -100,7 +101,7 @@ module PostgisFunctions
|
|
100
101
|
def distance_to(other)
|
101
102
|
postgis_calculate(:distance, [self, other]).to_f
|
102
103
|
end
|
103
|
-
|
104
|
+
|
104
105
|
#
|
105
106
|
# True if geometry A is completely inside geometry B.
|
106
107
|
#
|
@@ -136,11 +137,11 @@ module PostgisFunctions
|
|
136
137
|
postgis_calculate(:dwithin, [self, other], margin)
|
137
138
|
end
|
138
139
|
alias_method "in_bounds?", "d_within?"
|
139
|
-
|
140
|
+
|
140
141
|
#
|
141
|
-
# True if geometry B is completely inside geometry A.
|
142
|
+
# True if geometry B is completely inside geometry A.
|
142
143
|
#
|
143
|
-
# For this function to make sense, the source geometries must both be of the same
|
144
|
+
# For this function to make sense, the source geometries must both be of the same
|
144
145
|
# coordinate projection, having the same SRID. 'contains?' is the inverse of 'within?'.
|
145
146
|
#
|
146
147
|
# So a.contains?(b) is like b.within?(a) except in the case of invalid
|
@@ -156,7 +157,7 @@ module PostgisFunctions
|
|
156
157
|
def contains? other
|
157
158
|
postgis_calculate(:contains, [self, other])
|
158
159
|
end
|
159
|
-
|
160
|
+
|
160
161
|
#
|
161
162
|
# True if no point in Geometry A is outside Geometry B
|
162
163
|
#
|
@@ -177,7 +178,7 @@ module PostgisFunctions
|
|
177
178
|
postgis_calculate(:coveredby, [self, other])
|
178
179
|
end
|
179
180
|
alias_method "inside?", "covered_by?"
|
180
|
-
|
181
|
+
|
181
182
|
#
|
182
183
|
# Eye-candy. See 'covered_by?'.
|
183
184
|
#
|
@@ -230,8 +231,8 @@ module PostgisFunctions
|
|
230
231
|
def simplify(tolerance=0.1)
|
231
232
|
postgis_calculate(:simplify, self, tolerance)
|
232
233
|
end
|
233
|
-
|
234
|
-
|
234
|
+
|
235
|
+
|
235
236
|
def simplify!(tolerance=0.1)
|
236
237
|
#FIXME: not good..
|
237
238
|
self.update_attribute(get_column_name, simplify)
|
@@ -255,7 +256,7 @@ module PostgisFunctions
|
|
255
256
|
#
|
256
257
|
# True if Geometries "spatially intersect", share any portion of space.
|
257
258
|
# False if they don't (they are Disjoint).
|
258
|
-
#
|
259
|
+
#
|
259
260
|
# 'overlaps?', 'touches?', 'within?' all imply spatial intersection.
|
260
261
|
# If any of the aforementioned returns true, then the geometries also
|
261
262
|
# spatially intersect. 'disjoint?' implies false for spatial intersection.
|
@@ -344,11 +345,11 @@ module PostgisFunctions
|
|
344
345
|
def convex_hull
|
345
346
|
postgis_calculate(:convexhull, self)
|
346
347
|
end
|
347
|
-
|
348
|
+
|
348
349
|
#
|
349
350
|
# 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
|
351
|
+
# The return type can be a Polygon or MultiPolygon, depending on input.
|
352
|
+
# If the input lineworks do not form polygons NULL is returned. The inputs can
|
352
353
|
# be LINESTRINGS, MULTILINESTRINGS, POLYGONS, MULTIPOLYGONS, and GeometryCollections.
|
353
354
|
#
|
354
355
|
# Returns Boolean ST_BuildArea(geometry A);
|
@@ -356,7 +357,7 @@ module PostgisFunctions
|
|
356
357
|
def build_area
|
357
358
|
postgis_calculate(:buildarea, self)
|
358
359
|
end
|
359
|
-
|
360
|
+
|
360
361
|
#
|
361
362
|
# Returns true if this Geometry has no anomalous geometric points, such as
|
362
363
|
# self intersection or self tangency.
|
@@ -369,10 +370,10 @@ module PostgisFunctions
|
|
369
370
|
alias_method "simple?", "is_simple?"
|
370
371
|
|
371
372
|
#
|
372
|
-
# Aggregate. Creates a GeometryCollection containing possible polygons formed
|
373
|
+
# Aggregate. Creates a GeometryCollection containing possible polygons formed
|
373
374
|
# from the constituent linework of a set of geometries.
|
374
375
|
#
|
375
|
-
# Geometry Collections are often difficult to deal with with third party tools,
|
376
|
+
# Geometry Collections are often difficult to deal with with third party tools,
|
376
377
|
# so use ST_Polygonize in conjunction with ST_Dump to dump the polygons out into
|
377
378
|
# individual polygons.
|
378
379
|
#
|
@@ -381,75 +382,84 @@ module PostgisFunctions
|
|
381
382
|
def polygonize#(geom)
|
382
383
|
postgis_calculate(:polygonize, self)
|
383
384
|
end
|
384
|
-
|
385
|
+
|
385
386
|
#
|
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,
|
387
|
+
# Returns true if this Geometry is spatially related to anotherGeometry,
|
388
|
+
# by testing for intersections between the Interior, Boundary and Exterior
|
389
|
+
# of the two geometries as specified by the values in the
|
390
|
+
# intersectionPatternMatrix. If no intersectionPatternMatrix is passed in,
|
390
391
|
# 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
|
392
|
+
#
|
393
|
+
#
|
394
|
+
# Version 1: Takes geomA, geomB, intersectionMatrix and Returns 1 (TRUE) if
|
395
|
+
# this Geometry is spatially related to anotherGeometry, by testing for
|
396
|
+
# intersections between the Interior, Boundary and Exterior of the two
|
396
397
|
# geometries as specified by the values in the intersectionPatternMatrix.
|
397
|
-
#
|
398
|
-
# This is especially useful for testing compound checks of intersection,
|
398
|
+
#
|
399
|
+
# This is especially useful for testing compound checks of intersection,
|
399
400
|
# crosses, etc in one step.
|
400
|
-
#
|
401
|
+
#
|
401
402
|
# Do not call with a GeometryCollection as an argument
|
402
|
-
#
|
403
|
-
# This is the "allowable" version that returns a boolean, not an integer.
|
403
|
+
#
|
404
|
+
# This is the "allowable" version that returns a boolean, not an integer.
|
404
405
|
# 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
|
406
|
+
# This DOES NOT automagically include an index call. The reason for that
|
407
|
+
# is some relationships are anti e.g. Disjoint. If you are using a relationship
|
407
408
|
# pattern that requires intersection, then include the && index call.
|
408
|
-
#
|
409
|
-
# Version 2: Takes geomA and geomB and returns the DE-9IM
|
409
|
+
#
|
410
|
+
# Version 2: Takes geomA and geomB and returns the DE-9IM
|
410
411
|
# (dimensionally extended nine-intersection matrix)
|
411
|
-
#
|
412
|
+
#
|
412
413
|
# Do not call with a GeometryCollection as an argument
|
413
414
|
# Not in OGC spec, but implied. see s2.1.13.2
|
414
|
-
#
|
415
|
+
#
|
415
416
|
# Both Performed by the GEOS module
|
416
|
-
#
|
417
|
+
#
|
417
418
|
# Returns:
|
418
419
|
#
|
419
|
-
#
|
420
|
-
#
|
420
|
+
# String ST_Relate(geometry geomA, geometry geomB);
|
421
|
+
# Boolean ST_Relate(geometry geomA, geometry geomB, text intersectionPatternMatrix);
|
421
422
|
#
|
422
423
|
def relate?(other, m = nil)
|
423
424
|
# Relate is case sentitive.......
|
424
425
|
m = "'#{m}'" if m
|
425
426
|
postgis_calculate("Relate", [self, other], m)
|
426
|
-
end
|
427
|
+
end
|
427
428
|
|
428
|
-
#
|
429
|
-
# Transform the geometry into a different spatial reference system.
|
430
429
|
#
|
431
|
-
#
|
432
|
-
#
|
433
|
-
# ST_Transform is often confused with ST_SetSRID(). ST_Transform actually changes the coordinates of a geometry from one spatial reference system to another, while ST_SetSRID() simply changes the SRID identifier of the geometry
|
434
|
-
# Requires PostGIS be compiled with Proj support. Use PostGIS_Full_Version to confirm you have proj support compiled in.
|
435
|
-
#
|
436
|
-
# If using more than one transformation, it is useful to have a functional index on the commonly used transformations to take advantage of index usage.
|
430
|
+
# Transform the geometry into a different spatial reference system.
|
431
|
+
# The destination SRID must exist in the SPATIAL_REF_SYS table.
|
437
432
|
#
|
438
|
-
# Prior to 1.3.4, this function crashes if used with geometries that contain CURVES. This is fixed in 1.3.4+
|
439
433
|
# This method implements the OpenGIS Simple Features Implementation Specification for SQL.
|
440
|
-
# This method supports Circular Strings and Curves
|
434
|
+
# This method supports Circular Strings and Curves (PostGIS 1.3.4+)
|
435
|
+
#
|
436
|
+
# Requires PostGIS be compiled with Proj support.
|
437
|
+
#
|
438
|
+
# Return Geometry ST_Transform(geometry g1, integer srid);
|
441
439
|
#
|
442
440
|
def transform(new_srid)
|
443
441
|
postgis_calculate("Transform", self, new_srid)
|
444
442
|
end
|
445
|
-
|
443
|
+
|
444
|
+
#
|
445
|
+
# Returns a modified geometry having no segment longer than the given distance.
|
446
|
+
# Distance computation is performed in 2d only.
|
447
|
+
#
|
448
|
+
# This will only increase segments. It will not lengthen segments shorter than max length
|
449
|
+
#
|
450
|
+
# Return Geometry ST_Segmentize(geometry geomA, float max_length);
|
451
|
+
#
|
452
|
+
def segmentize(max_length=1.0)
|
453
|
+
postgis_calculate("segmentize", self, max_length)
|
454
|
+
end
|
455
|
+
|
446
456
|
#
|
447
457
|
# LINESTRING
|
448
458
|
#
|
449
459
|
#
|
450
460
|
#
|
451
461
|
module LineStringFunctions
|
452
|
-
|
462
|
+
|
453
463
|
#
|
454
464
|
# Returns the 2D length of the geometry if it is a linestring, multilinestring,
|
455
465
|
# ST_Curve, ST_MultiCurve. 0 is returned for areal geometries. For areal geometries
|
@@ -548,12 +558,12 @@ module PostgisFunctions
|
|
548
558
|
def locate_point point
|
549
559
|
postgis_calculate(:line_locate_point, [self, point]).to_f
|
550
560
|
end
|
551
|
-
|
561
|
+
|
552
562
|
#
|
553
|
-
# Return a derived geometry collection value with elements that match the
|
563
|
+
# Return a derived geometry collection value with elements that match the
|
554
564
|
# specified measure. Polygonal elements are not supported.
|
555
565
|
#
|
556
|
-
# Semantic is specified by: ISO/IEC CD 13249-3:200x(E) - Text for
|
566
|
+
# Semantic is specified by: ISO/IEC CD 13249-3:200x(E) - Text for
|
557
567
|
# Continuation CD Editing Meeting
|
558
568
|
#
|
559
569
|
# Returns geometry ST_Locate_Along_Measure(geometry ageom_with_measure, float a_measure);
|
@@ -561,9 +571,9 @@ module PostgisFunctions
|
|
561
571
|
def locate_along_measure(measure)
|
562
572
|
postgis_calculate(:locate_along_measure, self, measure)
|
563
573
|
end
|
564
|
-
|
574
|
+
|
565
575
|
#
|
566
|
-
# Return a derived geometry collection value with elements that match the
|
576
|
+
# Return a derived geometry collection value with elements that match the
|
567
577
|
# specified range of measures inclusively. Polygonal elements are not supported.
|
568
578
|
#
|
569
579
|
# Semantic is specified by: ISO/IEC CD 13249-3:200x(E) - Text for Continuation CD Editing Meeting
|
@@ -573,7 +583,7 @@ module PostgisFunctions
|
|
573
583
|
def locate_between_measures(a, b)
|
574
584
|
postgis_calculate(:locate_between_measures, self, [a,b])
|
575
585
|
end
|
576
|
-
|
586
|
+
|
577
587
|
#
|
578
588
|
# Returns a point interpolated along a line. First argument must be a LINESTRING.
|
579
589
|
# Second argument is a float8 between 0 and 1 representing fraction of total
|
@@ -603,7 +613,7 @@ module PostgisFunctions
|
|
603
613
|
postgis_calculate(:line_substring, self, [s, e])
|
604
614
|
end
|
605
615
|
|
606
|
-
###
|
616
|
+
###
|
607
617
|
#Not implemented in postgis yet
|
608
618
|
# ST_max_distance Returns the largest distance between two line strings.
|
609
619
|
#def max_distance other
|
@@ -699,7 +709,7 @@ module PostgisFunctions
|
|
699
709
|
end
|
700
710
|
|
701
711
|
end
|
702
|
-
|
712
|
+
|
703
713
|
###
|
704
714
|
##
|
705
715
|
#
|
@@ -713,10 +723,12 @@ module PostgisFunctions
|
|
713
723
|
# Return the area measurement of an ST_Surface or ST_MultiSurface value.
|
714
724
|
# Area is in the units of the spatial reference system.
|
715
725
|
#
|
726
|
+
# Accepts optional parameter, the srid to transform to.
|
727
|
+
#
|
716
728
|
# Returns Float ST_Area(geometry g1);
|
717
729
|
#
|
718
|
-
def area
|
719
|
-
postgis_calculate(:area, self).to_f
|
730
|
+
def area transform=nil
|
731
|
+
postgis_calculate(:area, self, { :transform => transform }).to_f
|
720
732
|
end
|
721
733
|
|
722
734
|
#
|
@@ -725,10 +737,12 @@ module PostgisFunctions
|
|
725
737
|
# use 'length'. Measurements are in the units of the spatial reference system of
|
726
738
|
# the geometry.
|
727
739
|
#
|
740
|
+
# Accepts optional parameter, the srid to transform to.
|
741
|
+
#
|
728
742
|
# Returns Float ST_Perimeter(geometry g1);
|
729
743
|
#
|
730
|
-
def perimeter
|
731
|
-
postgis_calculate(:perimeter, self).to_f
|
744
|
+
def perimeter transform=nil
|
745
|
+
postgis_calculate(:perimeter, self, { :transform => transform }).to_f
|
732
746
|
end
|
733
747
|
|
734
748
|
#
|
@@ -775,7 +789,7 @@ module PostgisFunctions
|
|
775
789
|
end
|
776
790
|
|
777
791
|
end
|
778
|
-
|
792
|
+
|
779
793
|
end
|
780
794
|
|
781
795
|
# NEW
|
data/lib/postgis_functions.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
#
|
1
|
+
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
3
|
# PostGIS Adapter - http://github.com/nofxx/postgis_adapter
|
4
4
|
#
|
5
|
-
# Hope you enjoy this plugin.
|
6
|
-
#
|
5
|
+
# Hope you enjoy this plugin.
|
7
6
|
#
|
8
7
|
#
|
9
8
|
# Post any bugs/suggestions to the lighthouse tracker:
|
@@ -16,12 +15,11 @@
|
|
16
15
|
# Earth Spheroid - http://en.wikipedia.org/wiki/Figure_of_the_Earth
|
17
16
|
#
|
18
17
|
#
|
19
|
-
#
|
20
18
|
module PostgisFunctions
|
21
19
|
EARTH_SPHEROID = "'SPHEROID[\"GRS-80\",6378137,298.257222101]'" # SRID => 4326
|
22
|
-
#EARTH_SPHEROID = "'SPHEROID[\"IERS_2003\",6378136.6,298.25642]'" # SRID =>
|
20
|
+
#EARTH_SPHEROID = "'SPHEROID[\"IERS_2003\",6378136.6,298.25642]'" # SRID =>
|
23
21
|
|
24
|
-
def postgis_calculate(operation, subjects, options =
|
22
|
+
def postgis_calculate(operation, subjects, options = {})
|
25
23
|
subjects = [subjects] unless subjects.respond_to?(:map)
|
26
24
|
return execute_geometrical_calculation(operation, subjects, options)
|
27
25
|
rescue Exception => e
|
@@ -33,14 +31,14 @@ module PostgisFunctions
|
|
33
31
|
def get_column_name
|
34
32
|
@geo_column ||= postgis_geoms[:columns].first
|
35
33
|
end
|
36
|
-
|
34
|
+
|
35
|
+
#
|
37
36
|
# Construct the postgis sql query
|
38
37
|
#
|
39
38
|
# Area return in square feet
|
40
39
|
# Distance/DWithin/Length/Perimeter — in projected units.
|
41
40
|
# DistanceSphere/Spheroid — in meters.
|
42
41
|
#
|
43
|
-
#
|
44
42
|
def construct_geometric_sql(type,geoms,options)
|
45
43
|
|
46
44
|
tables = geoms.map do |t| {
|
@@ -48,16 +46,24 @@ module PostgisFunctions
|
|
48
46
|
:uid => unique_identifier,
|
49
47
|
:id => t[:id] }
|
50
48
|
end
|
51
|
-
|
52
|
-
fields = tables.map { |f| "#{f[:uid]}.#{get_column_name}" } # 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
49
|
|
50
|
+
# Implement a better way for options?
|
51
|
+
if options.instance_of? Hash
|
52
|
+
transform = options.delete(:transform)
|
53
|
+
options = nil
|
54
|
+
end
|
55
|
+
|
56
|
+
fields = tables.map { |f| "#{f[:uid]}.#{get_column_name}" } # W1.geom
|
57
|
+
fields.map! { |f| "ST_Transform(#{f}, #{transform})" } if transform # ST_Transform(W1.geom,x)
|
58
|
+
conditions = tables.map { |f| "#{f[:uid]}.id = #{f[:id]}" } # W1.id = 5
|
59
|
+
tables.map! { |f| "#{f[:class]} #{f[:uid]}" } # streets W1
|
60
|
+
|
56
61
|
#
|
57
|
-
# Data
|
58
|
-
# BBox
|
59
|
-
#
|
60
|
-
|
62
|
+
# Data => SELECT Func(A,B)
|
63
|
+
# BBox => SELECT (A <=> B)
|
64
|
+
# Func => SELECT Func(Func(A))
|
65
|
+
#
|
66
|
+
if type != :bbox
|
61
67
|
opcode = type.to_s
|
62
68
|
opcode = "ST_#{opcode}" unless opcode =~ /th3d|pesinter/
|
63
69
|
fields << options if options
|
@@ -85,7 +91,7 @@ module PostgisFunctions
|
|
85
91
|
value = connection.select_value(construct_geometric_sql(operation, subject, options))
|
86
92
|
return nil unless value
|
87
93
|
if value =~ /t|f/
|
88
|
-
{"f" => false, "t" => true}[value]
|
94
|
+
{"f" => false, "t" => true}[value]
|
89
95
|
else
|
90
96
|
GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(value) rescue value
|
91
97
|
end
|
@@ -143,7 +149,7 @@ end
|
|
143
149
|
#ST_AsGML
|
144
150
|
#ST_AsKML
|
145
151
|
#ST_AsSVG
|
146
|
-
#
|
152
|
+
#
|
147
153
|
# def distance_convert(value, unit, from = nil)
|
148
154
|
# factor = case unit
|
149
155
|
# when :km, :kilo then 1
|
data/postgis_adapter.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{postgis_adapter}
|
5
|
-
s.version = "0.2.
|
5
|
+
s.version = "0.2.3"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Marcos Piccinini"]
|
9
|
-
s.date = %q{2009-
|
9
|
+
s.date = %q{2009-02-16}
|
10
10
|
s.description = %q{Postgis Adapter for Activer Record}
|
11
11
|
s.email = ["x@nofxx.com"]
|
12
12
|
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.rdoc"]
|
@@ -1,51 +1,45 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
2
|
|
3
|
-
|
4
3
|
describe "Point" do
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|
5
|
+
before(:all) do
|
6
|
+
@c1 ||= City.create!(:data => "City1", :geom => Polygon.from_coordinates([[[12,45],[45,41],[4,1],[12,45]],[[2,5],[5,1],[14,1],[2,5]]],4326))
|
7
|
+
@c2 ||= City.create!(:data => "City1", :geom => Polygon.from_coordinates([[[22,66],[65,65],[20,10],[22,66]],[[10,15],[15,11],[34,14],[10,15]]],4326))
|
8
|
+
@c3 ||= City.create!(:data => "City3", :geom => Polygon.from_coordinates([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]],4326))
|
9
|
+
@s1 ||= Street.create!(:data => "Street1", :geom => LineString.from_coordinates([[1,1],[2,2]],4326))
|
10
|
+
@s2 ||= Street.create!(:data => "Street2", :geom => LineString.from_coordinates([[4,4],[7,7]],4326))
|
11
|
+
@s3 ||= Street.create!(:data => "Street3", :geom => LineString.from_coordinates([[8,8],[18,18],[20,20],[25,25],[30,30],[38,38]],4326))
|
12
|
+
@s4 ||= Street.create!(:data => "Street3", :geom => LineString.from_coordinates([[10,8],[15,18]],4326))
|
13
|
+
@p1 ||= Position.create!(:data => "Point1", :geom => Point.from_x_y(1,1,4326))
|
14
|
+
@p2 ||= Position.create!(:data => "Point2", :geom => Point.from_x_y(5,5,4326))
|
15
|
+
@p3 ||= Position.create!(:data => "Point3", :geom => Point.from_x_y(8,8,4326))
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "BBox operations" do
|
19
|
+
|
20
|
+
it "should check stricly left" do
|
21
|
+
@p1.bbox("<<", @c1).should be_true
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should check stricly right" do
|
25
|
+
@p1.bbox(">>", @c1).should be_false
|
26
|
+
end
|
27
|
+
|
28
|
+
it { @p1.should be_strictly_left_of(@c1) }
|
29
|
+
it { @p1.should_not be_strictly_right_of(@c1) }
|
30
|
+
it { @p1.should_not be_overlaps_or_right_of(@c1) }
|
31
|
+
it { @p1.should be_overlaps_or_left_of(@c1) }
|
32
|
+
it { @p1.should_not be_completely_contained_by(@c1) }
|
33
|
+
it { @c2.completely_contains?(@p1).should be_false }
|
34
|
+
it { @p1.should be_overlaps_or_above(@c1) }
|
35
|
+
it { @p1.should be_overlaps_or_below(@c1) }
|
36
|
+
it { @p1.should_not be_strictly_above(@c1) }
|
37
|
+
it { @p1.should_not be_strictly_below(@c1) }
|
38
|
+
it { @p1.interacts_with?(@c1).should be_false }
|
39
|
+
|
40
|
+
it { @p1.binary_equal?(@c1).should be_false }
|
41
|
+
it { @p1.same_as?(@c1).should be_false }
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -39,28 +39,28 @@ describe "Common Functions" do
|
|
39
39
|
it { @p1.distance_spheroid_to(@p2).should be_close(627129.50,0.01) }
|
40
40
|
it { @p1.distance_spheroid_to(@p2).should be_close(627129.502639041, 0.000001) }
|
41
41
|
it { @p1.distance_spheroid_to(@p3).should be_close(1096324.48117672, 0.000001) }
|
42
|
-
|
42
|
+
|
43
43
|
it { @p1.should_not be_inside(@c1) }
|
44
44
|
it { @p1.should be_outside(@c1) }
|
45
45
|
it { @p1.should be_inside_circle(2.0,2.0,20.0) }
|
46
46
|
it { @p1.should_not be_inside_circle(50,50,2) }
|
47
47
|
it { @p1.should be_in_bounds(@s1) }
|
48
48
|
it { @p3.should_not be_in_bounds(@s1, 1) }
|
49
|
-
it { @p4.in_bounds?(@s3, 0.01).should be_false }
|
50
|
-
|
49
|
+
it { @p4.in_bounds?(@s3, 0.01).should be_false }
|
50
|
+
|
51
51
|
it { @p1.azimuth(@p2).should be_close(0.785398163397448,0.000001) }
|
52
52
|
it { @p1.azimuth(@s2).should raise_error }
|
53
53
|
it { @p1.disjoint?(@s2).should be_true }
|
54
54
|
it { @p3.polygonize.geometries.should be_empty }
|
55
|
-
it { @p4.where_on_line(@s3).should be_close(0.335, 0.0001) }
|
55
|
+
it { @p4.where_on_line(@s3).should be_close(0.335, 0.0001) }
|
56
56
|
it { @s3.locate_point(@p4).should be_close(0.335, 0.1)}
|
57
57
|
it { @s3.interpolate_point(0.335).x.should be_close(18.05, 0.01) }
|
58
|
-
|
58
|
+
|
59
59
|
it { @p1.relate?(@s3, "T*T***FF*").should be_false }
|
60
60
|
it { @p1.relate?(@s3).should eql("FF0FFF102") }
|
61
61
|
|
62
62
|
it "should transform srid" do
|
63
|
-
@p1.geom = @p1.transform(29101)
|
63
|
+
@p1.geom = @p1.transform(29101)
|
64
64
|
@p1.geom.srid.should eql(29101)
|
65
65
|
end
|
66
66
|
|
@@ -68,6 +68,10 @@ describe "Common Functions" do
|
|
68
68
|
@p1.where_on_line(@s1).should eql(0.0)
|
69
69
|
end
|
70
70
|
|
71
|
+
it "should see in what fraction of the ls it is" do
|
72
|
+
@p2.where_on_line(@s2).should be_close(0.3333, 0.1)
|
73
|
+
end
|
74
|
+
|
71
75
|
end
|
72
76
|
|
73
77
|
describe "Polygon" do
|
@@ -83,14 +87,18 @@ describe "Common Functions" do
|
|
83
87
|
it "should find one city (first) that contains a point" do
|
84
88
|
City.contain(@p4.geom, 4326).data.should eql("City1")
|
85
89
|
end
|
86
|
-
|
90
|
+
|
87
91
|
it { @c2.should be_closed }
|
92
|
+
it { @c2.dimension.should eql(2) }
|
93
|
+
|
88
94
|
it { @c3.area.should be_close(1093.270089, 0.1) }
|
89
95
|
it { @c2.area.should be_close(1159.5, 0.1) }
|
96
|
+
it { @c2.area(32640).should be_close(5852791139841.2, 0.01) }
|
90
97
|
|
91
|
-
it { @c2.dimension.should eql(2) }
|
92
98
|
it { @c2.perimeter.should be_close(219.770013855493, 0.1) }
|
99
|
+
it { @c2.perimeter(32640).should be_close(23061464.4268903, 0.1) }
|
93
100
|
it { @c2.perimeter3d.should be_close(219.770013855493, 0.1) }
|
101
|
+
|
94
102
|
it { @c1.contains?(@p1).should be_false }
|
95
103
|
it { @c1.contains?(@p4).should be_true }
|
96
104
|
|
@@ -143,9 +151,14 @@ describe "Common Functions" do
|
|
143
151
|
it { @c2.disjoint?(@p2).should be_true }
|
144
152
|
it { @c3.polygonize.should have(2).geometries }
|
145
153
|
|
154
|
+
it "should acts as jack" do
|
155
|
+
@c2.segmentize(0.1).should be_instance_of(Polygon)
|
156
|
+
end
|
157
|
+
|
158
|
+
|
146
159
|
# weird...
|
147
160
|
# it do
|
148
|
-
# @c1.disjoint?(@
|
161
|
+
# @c1.disjoint?(@p2).should be_true
|
149
162
|
# end
|
150
163
|
|
151
164
|
end
|
@@ -241,7 +254,7 @@ describe "Common Functions" do
|
|
241
254
|
|
242
255
|
it do @s1.locate_point(@p1).should eql(0.0) end
|
243
256
|
it do @s1.locate_point(@p2).should eql(1.0) end
|
244
|
-
|
257
|
+
|
245
258
|
it "should simplify a line" do
|
246
259
|
@s3.simplify.points.length.should eql(2)
|
247
260
|
end
|
@@ -258,7 +271,7 @@ describe "Common Functions" do
|
|
258
271
|
it { @s1.overlaps?(@s2).should be_false }
|
259
272
|
it { @s1.convex_hull.should be_instance_of(LineString) }
|
260
273
|
it { @s1.line_substring(0.2,0.5).should be_instance_of(LineString) }
|
261
|
-
|
274
|
+
|
262
275
|
it do
|
263
276
|
@s1.interpolate_point(0.7).should be_instance_of(Point)
|
264
277
|
@s1.interpolate_point(0.7).x.should be_close(1.7,0.1)
|
@@ -275,7 +288,10 @@ describe "Common Functions" do
|
|
275
288
|
@s2.build_area.should be_nil
|
276
289
|
end
|
277
290
|
|
291
|
+
it "should acts as jack" do
|
292
|
+
@s2.segmentize(0.1).should be_instance_of(LineString)
|
293
|
+
end
|
294
|
+
|
278
295
|
end
|
279
296
|
|
280
297
|
end
|
281
|
-
|
@@ -2,22 +2,32 @@ require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
2
|
|
3
3
|
describe "PostgisFunctions" do
|
4
4
|
before(:all) do
|
5
|
-
#load_schema
|
6
|
-
@c1 ||= City.create!(:data => "City1", :geom => Polygon.from_coordinates([[[12,45],[45,
|
5
|
+
#load_schema
|
6
|
+
@c1 ||= City.create!(:data => "City1", :geom => Polygon.from_coordinates([[[12,45],[45,42],[4,1],[12,45]],[[2,5],[5,1],[14,1],[2,5]]],4326))
|
7
7
|
@s1 ||= Street.create!(:data => "Street1", :geom => LineString.from_coordinates([[-43,-20],[-42,-28]],4326))
|
8
|
-
@p1 ||= Position.create!(:data => "Point1", :geom => Point.from_x_y(-43,-22,4326))
|
8
|
+
@p1 ||= Position.create!(:data => "Point1", :geom => Point.from_x_y(-43,-22,4326))
|
9
9
|
end
|
10
10
|
|
11
11
|
describe "Common Mix" do
|
12
|
-
|
12
|
+
|
13
13
|
it "should calculate distance point to line" do
|
14
14
|
@p1.distance_to(@s1).should be_close(0.248069469178417, 0.00000001)
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
it "should calculate inside a city" do
|
18
18
|
@p1.should_not be_inside(@c1)
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
|
+
it { @c1.area(32640).should be_close(9165235788987.37, 0.01) }
|
22
|
+
|
23
|
+
it { @c1.area.should be_close(720.0, 0.1) }
|
24
|
+
|
25
|
+
it { @p1.should be_strictly_left_of(@c1) }
|
26
|
+
|
27
|
+
it { @s1.length.should be_close(8.06225774829855, 0.001) }
|
28
|
+
|
29
|
+
it { @s1.length_spheroid.should be_close(891883.597963462,0.0001) }
|
30
|
+
|
21
31
|
end
|
22
32
|
|
23
33
|
#TODO is sorted rspec helper
|
data/spec/spec.opts
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: postgis_adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marcos Piccinini
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-02-16 00:00:00 -03:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|