postgis_adapter 0.2.1 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|