ppe-georuby 1.7.2
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/.gitignore +6 -0
- data/History.txt +4 -0
- data/LICENSE +21 -0
- data/README.txt +118 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/lib/geo_ruby.rb +22 -0
- data/lib/geo_ruby/gpx.rb +1 -0
- data/lib/geo_ruby/gpx4r/gpx.rb +117 -0
- data/lib/geo_ruby/shp.rb +1 -0
- data/lib/geo_ruby/shp4r/dbf.rb +41 -0
- data/lib/geo_ruby/shp4r/shp.rb +697 -0
- data/lib/geo_ruby/simple_features/envelope.rb +167 -0
- data/lib/geo_ruby/simple_features/ewkb_parser.rb +216 -0
- data/lib/geo_ruby/simple_features/ewkt_parser.rb +336 -0
- data/lib/geo_ruby/simple_features/geometry.rb +228 -0
- data/lib/geo_ruby/simple_features/geometry_collection.rb +136 -0
- data/lib/geo_ruby/simple_features/geometry_factory.rb +81 -0
- data/lib/geo_ruby/simple_features/georss_parser.rb +135 -0
- data/lib/geo_ruby/simple_features/helper.rb +18 -0
- data/lib/geo_ruby/simple_features/line_string.rb +206 -0
- data/lib/geo_ruby/simple_features/linear_ring.rb +12 -0
- data/lib/geo_ruby/simple_features/multi_line_string.rb +51 -0
- data/lib/geo_ruby/simple_features/multi_point.rb +46 -0
- data/lib/geo_ruby/simple_features/multi_polygon.rb +52 -0
- data/lib/geo_ruby/simple_features/point.rb +364 -0
- data/lib/geo_ruby/simple_features/polygon.rb +157 -0
- data/ppe-georuby.gemspec +133 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +82 -0
- data/spec/data/gpx/fells_loop.gpx +1077 -0
- data/spec/data/gpx/long.gpx +1642 -0
- data/spec/data/gpx/long.kml +31590 -0
- data/spec/data/gpx/long.nmea +2220 -0
- data/spec/data/gpx/short.gpx +13634 -0
- data/spec/data/gpx/short.kml +130 -0
- data/spec/data/gpx/tracktreks.gpx +706 -0
- data/spec/data/multipoint.dbf +0 -0
- data/spec/data/multipoint.shp +0 -0
- data/spec/data/multipoint.shx +0 -0
- data/spec/data/point.dbf +0 -0
- data/spec/data/point.shp +0 -0
- data/spec/data/point.shx +0 -0
- data/spec/data/polygon.dbf +0 -0
- data/spec/data/polygon.shp +0 -0
- data/spec/data/polygon.shx +0 -0
- data/spec/data/polyline.dbf +0 -0
- data/spec/data/polyline.shp +0 -0
- data/spec/data/polyline.shx +0 -0
- data/spec/geo_ruby/gpx4r/gpx_spec.rb +106 -0
- data/spec/geo_ruby/shp4r/shp_spec.rb +240 -0
- data/spec/geo_ruby/simple_features/envelope_spec.rb +45 -0
- data/spec/geo_ruby/simple_features/ewkb_parser_spec.rb +158 -0
- data/spec/geo_ruby/simple_features/ewkt_parser_spec.rb +179 -0
- data/spec/geo_ruby/simple_features/geometry_collection_spec.rb +55 -0
- data/spec/geo_ruby/simple_features/geometry_factory_spec.rb +11 -0
- data/spec/geo_ruby/simple_features/geometry_spec.rb +32 -0
- data/spec/geo_ruby/simple_features/georss_parser_spec.rb +218 -0
- data/spec/geo_ruby/simple_features/line_string_spec.rb +245 -0
- data/spec/geo_ruby/simple_features/linear_ring_spec.rb +14 -0
- data/spec/geo_ruby/simple_features/multi_line_string_spec.rb +54 -0
- data/spec/geo_ruby/simple_features/multi_point_spec.rb +35 -0
- data/spec/geo_ruby/simple_features/multi_polygon_spec.rb +50 -0
- data/spec/geo_ruby/simple_features/point_spec.rb +356 -0
- data/spec/geo_ruby/simple_features/polygon_spec.rb +108 -0
- data/spec/geo_ruby_spec.rb +27 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +65 -0
- metadata +162 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module GeoRuby
|
2
|
+
module SimpleFeatures
|
3
|
+
#indicates the presence of Z coordinates in EWKB strings
|
4
|
+
Z_MASK=0x80000000
|
5
|
+
#indicates the presence of M coordinates in EWKB strings.
|
6
|
+
M_MASK=0x40000000
|
7
|
+
#indicate the presence of a SRID in EWKB strings.
|
8
|
+
SRID_MASK=0x20000000
|
9
|
+
#GeoRSS namespace
|
10
|
+
GEORSS_NS = "http://www.georss.org/georss"
|
11
|
+
#GML Namespace
|
12
|
+
GML_NS = "http://www.opengis.net/gml"
|
13
|
+
#W3CGeo Namespace
|
14
|
+
W3CGEO_NS = "http://www.w3.org/2003/01/geo/wgs84_pos#"
|
15
|
+
#KML Namespace
|
16
|
+
KML_NS = "http://earth.google.com/kml/2.1"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
require "geo_ruby/simple_features/geometry"
|
2
|
+
|
3
|
+
module GeoRuby
|
4
|
+
module SimpleFeatures
|
5
|
+
#Represents a line string as an array of points (see Point).
|
6
|
+
class LineString < Geometry
|
7
|
+
#the list of points forming the line string
|
8
|
+
attr_reader :points
|
9
|
+
|
10
|
+
def initialize(srid= DEFAULT_SRID,with_z=false,with_m=false)
|
11
|
+
super(srid,with_z,with_m)
|
12
|
+
@points=[]
|
13
|
+
end
|
14
|
+
|
15
|
+
#Delegate the unknown methods to the points array
|
16
|
+
def method_missing(method_name,*args,&b)
|
17
|
+
@points.send(method_name,*args,&b)
|
18
|
+
end
|
19
|
+
|
20
|
+
#tests if the line string is closed
|
21
|
+
def is_closed
|
22
|
+
#a bit naive...
|
23
|
+
@points.first == @points.last
|
24
|
+
end
|
25
|
+
alias :closed? :is_closed
|
26
|
+
|
27
|
+
#Bounding box in 2D/3D. Returns an array of 2 points
|
28
|
+
def bounding_box
|
29
|
+
max_x, min_x, max_y, min_y = -Float::MAX, Float::MAX, -Float::MAX, Float::MAX
|
30
|
+
if(with_z)
|
31
|
+
max_z, min_z = -Float::MAX,Float::MAX
|
32
|
+
each do |point|
|
33
|
+
max_y = point.y if point.y > max_y
|
34
|
+
min_y = point.y if point.y < min_y
|
35
|
+
max_x = point.x if point.x > max_x
|
36
|
+
min_x = point.x if point.x < min_x
|
37
|
+
max_z = point.z if point.z > max_z
|
38
|
+
min_z = point.z if point.z < min_z
|
39
|
+
end
|
40
|
+
[Point.from_x_y_z(min_x,min_y,min_z),Point.from_x_y_z(max_x,max_y,max_z)]
|
41
|
+
else
|
42
|
+
each do |point|
|
43
|
+
max_y = point.y if point.y > max_y
|
44
|
+
min_y = point.y if point.y < min_y
|
45
|
+
max_x = point.x if point.x > max_x
|
46
|
+
min_x = point.x if point.x < min_x
|
47
|
+
end
|
48
|
+
[Point.from_x_y(min_x,min_y),Point.from_x_y(max_x,max_y)]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def m_range
|
53
|
+
if with_m
|
54
|
+
max_m, min_m = -Float::MAX, Float::MAX
|
55
|
+
each do |point|
|
56
|
+
max_m = point.m if point.m > max_m
|
57
|
+
min_m = point.m if point.m < min_m
|
58
|
+
end
|
59
|
+
[min_m,max_m]
|
60
|
+
else
|
61
|
+
[0,0]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
#call to native Geo intersect, return true or false
|
66
|
+
def intersects?(other_line_string)
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
def spherical_distance
|
71
|
+
total = 0
|
72
|
+
@points.each_with_index do |p,i|
|
73
|
+
total += p.spherical_distance(@points[i+1]) if @points[i+1]
|
74
|
+
end
|
75
|
+
total
|
76
|
+
end
|
77
|
+
|
78
|
+
def euclidian_distance
|
79
|
+
total = 0
|
80
|
+
@points.each_with_index do |p,i|
|
81
|
+
total += p.euclidian_distance(@points[i+1]) if @points[i+1]
|
82
|
+
end
|
83
|
+
total
|
84
|
+
end
|
85
|
+
|
86
|
+
#Tests the equality of line strings
|
87
|
+
def ==(other_line_string)
|
88
|
+
if(other_line_string.class != self.class or
|
89
|
+
other_line_string.length != self.length)
|
90
|
+
false
|
91
|
+
else
|
92
|
+
index=0
|
93
|
+
while index<length
|
94
|
+
return false if self[index] != other_line_string[index]
|
95
|
+
index+=1
|
96
|
+
end
|
97
|
+
true
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
#Binary representation of a line string
|
102
|
+
def binary_representation(allow_z=true,allow_m=true) #:nodoc:
|
103
|
+
rep = [length].pack("V")
|
104
|
+
each {|point| rep << point.binary_representation(allow_z,allow_m) }
|
105
|
+
rep
|
106
|
+
end
|
107
|
+
|
108
|
+
#WKB geometry type
|
109
|
+
def binary_geometry_type #:nodoc:
|
110
|
+
2
|
111
|
+
end
|
112
|
+
|
113
|
+
#Text representation of a line string
|
114
|
+
def text_representation(allow_z=true,allow_m=true) #:nodoc:
|
115
|
+
@points.collect{|point| point.text_representation(allow_z,allow_m) }.join(",")
|
116
|
+
end
|
117
|
+
#WKT geometry type
|
118
|
+
def text_geometry_type #:nodoc:
|
119
|
+
"LINESTRING"
|
120
|
+
end
|
121
|
+
|
122
|
+
#georss simple representation
|
123
|
+
def georss_simple_representation(options) #:nodoc:
|
124
|
+
georss_ns = options[:georss_ns] || "georss"
|
125
|
+
geom_attr = options[:geom_attr]
|
126
|
+
"<#{georss_ns}:line#{geom_attr}>" + georss_poslist + "</#{georss_ns}:line>\n"
|
127
|
+
end
|
128
|
+
#georss w3c representation : outputs the first point of the line
|
129
|
+
def georss_w3cgeo_representation(options) #:nodoc:
|
130
|
+
w3cgeo_ns = options[:w3cgeo_ns] || "geo"
|
131
|
+
"<#{w3cgeo_ns}:lat>#{self[0].y}</#{w3cgeo_ns}:lat>\n<#{w3cgeo_ns}:long>#{self[0].x}</#{w3cgeo_ns}:long>\n"
|
132
|
+
end
|
133
|
+
#georss gml representation
|
134
|
+
def georss_gml_representation(options) #:nodoc:
|
135
|
+
georss_ns = options[:georss_ns] || "georss"
|
136
|
+
gml_ns = options[:gml_ns] || "gml"
|
137
|
+
|
138
|
+
result = "<#{georss_ns}:where>\n<#{gml_ns}:LineString>\n<#{gml_ns}:posList>\n"
|
139
|
+
result += georss_poslist
|
140
|
+
result += "\n</#{gml_ns}:posList>\n</#{gml_ns}:LineString>\n</#{georss_ns}:where>\n"
|
141
|
+
end
|
142
|
+
|
143
|
+
def georss_poslist #:nodoc:
|
144
|
+
map {|point| "#{point.y} #{point.x}"}.join(" ")
|
145
|
+
end
|
146
|
+
|
147
|
+
#outputs the geometry in kml format : options are <tt>:id</tt>, <tt>:tesselate</tt>, <tt>:extrude</tt>,
|
148
|
+
#<tt>:altitude_mode</tt>. If the altitude_mode option is not present, the Z (if present) will not be output (since
|
149
|
+
#it won't be used by GE anyway: clampToGround is the default)
|
150
|
+
def kml_representation(options = {}) #:nodoc:
|
151
|
+
result = "<LineString#{options[:id_attr]}>\n"
|
152
|
+
result += options[:geom_data] if options[:geom_data]
|
153
|
+
result += "<coordinates>"
|
154
|
+
result += kml_poslist(options)
|
155
|
+
result += "</coordinates>\n"
|
156
|
+
result += "</LineString>\n"
|
157
|
+
end
|
158
|
+
|
159
|
+
def kml_poslist(options) #:nodoc:
|
160
|
+
pos_list = if options[:allow_z]
|
161
|
+
map {|point| "#{point.x},#{point.y},#{options[:fixed_z] || point.z || 0}" }
|
162
|
+
else
|
163
|
+
map {|point| "#{point.x},#{point.y}" }
|
164
|
+
end
|
165
|
+
pos_list.reverse! if(options[:reverse])
|
166
|
+
pos_list.join(" ")
|
167
|
+
end
|
168
|
+
|
169
|
+
# Simplify linestring (Douglas Peucker Algorithm)
|
170
|
+
# http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
|
171
|
+
def simplify(epsilon=1)
|
172
|
+
LineString.from_points(do_simplify(@points, epsilon))
|
173
|
+
end
|
174
|
+
|
175
|
+
def do_simplify(list, epsilon)
|
176
|
+
index = dmax = 0
|
177
|
+
2.upto(list.length - 1) do |i|
|
178
|
+
d = list[i].orthogonal_distance(list[0], list[-1])
|
179
|
+
index, dmax = i, d if d > dmax
|
180
|
+
end
|
181
|
+
|
182
|
+
if dmax >= epsilon
|
183
|
+
res1 = do_simplify(list[0..index], epsilon)
|
184
|
+
res2 = do_simplify(list[index..-1], epsilon)
|
185
|
+
res1[0..-2] + res2[0..-1]
|
186
|
+
else
|
187
|
+
[list[0], list[-1]]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
#Creates a new line string. Accept an array of points as argument
|
192
|
+
def self.from_points(points,srid=DEFAULT_SRID,with_z=false,with_m=false)
|
193
|
+
line_string = new(srid,with_z,with_m)
|
194
|
+
line_string.concat(points)
|
195
|
+
line_string
|
196
|
+
end
|
197
|
+
|
198
|
+
#Creates a new line string. Accept a sequence of points as argument : ((x,y)...(x,y))
|
199
|
+
def self.from_coordinates(points,srid=DEFAULT_SRID,with_z=false,with_m=false)
|
200
|
+
line_string = new(srid,with_z,with_m)
|
201
|
+
line_string.concat( points.map {|p| Point.from_coordinates(p,srid,with_z,with_m) } )
|
202
|
+
line_string
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'geo_ruby/simple_features/line_string'
|
2
|
+
|
3
|
+
module GeoRuby
|
4
|
+
module SimpleFeatures
|
5
|
+
#Represents a linear ring, which is a closed line string (see LineString). Currently, no check is performed to verify if the linear ring is really closed.
|
6
|
+
class LinearRing < LineString
|
7
|
+
def initialize(srid= DEFAULT_SRID,with_z=false,with_m=false)
|
8
|
+
super(srid,with_z,with_m)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'geo_ruby/simple_features/geometry_collection'
|
2
|
+
|
3
|
+
module GeoRuby
|
4
|
+
|
5
|
+
module SimpleFeatures
|
6
|
+
|
7
|
+
#Represents a group of line strings (see LineString).
|
8
|
+
class MultiLineString < GeometryCollection
|
9
|
+
|
10
|
+
def initialize(srid = DEFAULT_SRID,with_z=false,with_m=false)
|
11
|
+
super(srid)
|
12
|
+
end
|
13
|
+
|
14
|
+
def binary_geometry_type #:nodoc:
|
15
|
+
5
|
16
|
+
end
|
17
|
+
|
18
|
+
def points
|
19
|
+
geometries.map(&:points).flatten
|
20
|
+
end
|
21
|
+
|
22
|
+
#Text representation of a multi line string
|
23
|
+
def text_representation(allow_z=true,allow_m=true) #:nodoc:
|
24
|
+
@geometries.collect{|line_string| "(" + line_string.text_representation(allow_z,allow_m) + ")" }.join(",")
|
25
|
+
end
|
26
|
+
|
27
|
+
#WKT geometry type
|
28
|
+
def text_geometry_type #:nodoc:
|
29
|
+
"MULTILINESTRING"
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_line_string(join = true)
|
33
|
+
LineString.from_points(points)
|
34
|
+
end
|
35
|
+
|
36
|
+
#Creates a new multi line string from an array of line strings
|
37
|
+
def self.from_line_strings(line_strings,srid=DEFAULT_SRID,with_z=false,with_m=false)
|
38
|
+
multi_line_string = new(srid,with_z,with_m)
|
39
|
+
multi_line_string.concat(line_strings)
|
40
|
+
multi_line_string
|
41
|
+
end
|
42
|
+
|
43
|
+
#Creates a new multi line string from sequences of points : (((x,y)...(x,y)),((x,y)...(x,y)))
|
44
|
+
def self.from_coordinates(point_sequences,srid=DEFAULT_SRID,with_z=false,with_m=false)
|
45
|
+
multi_line_string = new(srid,with_z,with_m)
|
46
|
+
multi_line_string.concat(point_sequences.collect {|points| LineString.from_coordinates(points,srid,with_z,with_m) })
|
47
|
+
multi_line_string
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'geo_ruby/simple_features/geometry_collection'
|
2
|
+
|
3
|
+
module GeoRuby
|
4
|
+
module SimpleFeatures
|
5
|
+
#Represents a group of points (see Point).
|
6
|
+
class MultiPoint < GeometryCollection
|
7
|
+
|
8
|
+
def initialize(srid= DEFAULT_SRID,with_z=false,with_m=false)
|
9
|
+
super(srid,with_z,with_m)
|
10
|
+
end
|
11
|
+
|
12
|
+
def binary_geometry_type #:nodoc:
|
13
|
+
4
|
14
|
+
end
|
15
|
+
|
16
|
+
def points
|
17
|
+
@geometries
|
18
|
+
end
|
19
|
+
|
20
|
+
#Text representation of a MultiPoint
|
21
|
+
def text_representation(allow_z=true,allow_m=true) #:nodoc:
|
22
|
+
"(" + @geometries.collect{|point| point.text_representation(allow_z,allow_m)}.join("),(") + ")"
|
23
|
+
end
|
24
|
+
|
25
|
+
#WKT geoemtry type
|
26
|
+
def text_geometry_type #:nodoc:
|
27
|
+
"MULTIPOINT"
|
28
|
+
end
|
29
|
+
|
30
|
+
#Creates a new multi point from an array of points
|
31
|
+
def self.from_points(points,srid= DEFAULT_SRID,with_z=false,with_m=false)
|
32
|
+
multi_point= new(srid,with_z,with_m)
|
33
|
+
multi_point.concat(points)
|
34
|
+
multi_point
|
35
|
+
end
|
36
|
+
|
37
|
+
#Creates a new multi point from a list of point coordinates : ((x,y)...(x,y))
|
38
|
+
def self.from_coordinates(points,srid= DEFAULT_SRID,with_z=false,with_m=false)
|
39
|
+
multi_point= new(srid,with_z,with_m)
|
40
|
+
multi_point.concat(points.collect {|point| Point.from_coordinates(point,srid,with_z,with_m)})
|
41
|
+
multi_point
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'geo_ruby/simple_features/geometry_collection'
|
2
|
+
|
3
|
+
module GeoRuby
|
4
|
+
|
5
|
+
module SimpleFeatures
|
6
|
+
|
7
|
+
#Represents a group of polygons (see Polygon).
|
8
|
+
class MultiPolygon < GeometryCollection
|
9
|
+
|
10
|
+
def initialize(srid = DEFAULT_SRID,with_z=false,with_m=false)
|
11
|
+
super(srid)
|
12
|
+
end
|
13
|
+
|
14
|
+
def binary_geometry_type #:nodoc:
|
15
|
+
6
|
16
|
+
end
|
17
|
+
|
18
|
+
def points
|
19
|
+
@points ||= geometries.inject([]) do |arr, r|
|
20
|
+
arr.concat(r.rings.map(&:points).flatten)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#Text representation of a MultiPolygon
|
25
|
+
def text_representation(allow_z=true,allow_m=true) #:nodoc:
|
26
|
+
@geometries.map {|polygon| "(" + polygon.text_representation(allow_z,allow_m) + ")"}.join(",")
|
27
|
+
end
|
28
|
+
|
29
|
+
#WKT geometry type
|
30
|
+
def text_geometry_type #:nodoc:
|
31
|
+
"MULTIPOLYGON"
|
32
|
+
end
|
33
|
+
|
34
|
+
#Creates a multi polygon from an array of polygons
|
35
|
+
def self.from_polygons(polygons,srid=DEFAULT_SRID,with_z=false,with_m=false)
|
36
|
+
multi_polygon = new(srid,with_z,with_m)
|
37
|
+
multi_polygon.concat(polygons)
|
38
|
+
multi_polygon
|
39
|
+
end
|
40
|
+
|
41
|
+
#Creates a multi polygon from sequences of points : ((((x,y)...(x,y)),((x,y)...(x,y)),((x,y)...(x,y)))
|
42
|
+
def self.from_coordinates(point_sequence_sequences,srid= DEFAULT_SRID,with_z=false,with_m=false)
|
43
|
+
multi_polygon = new(srid,with_z,with_m)
|
44
|
+
multi_polygon.concat( point_sequence_sequences.collect {|point_sequences| Polygon.from_coordinates(point_sequences,srid,with_z,with_m) } )
|
45
|
+
multi_polygon
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,364 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require "geo_ruby/simple_features/geometry"
|
3
|
+
|
4
|
+
module GeoRuby
|
5
|
+
module SimpleFeatures
|
6
|
+
#Represents a point. It is in 3D if the Z coordinate is not +nil+.
|
7
|
+
class Point < Geometry
|
8
|
+
DEG2RAD = 0.0174532925199433
|
9
|
+
HALFPI = 1.5707963267948966
|
10
|
+
attr_accessor :x,:y,:z,:m
|
11
|
+
attr_reader :r, :t # radium and theta
|
12
|
+
|
13
|
+
#if you prefer calling the coordinates lat and lon (or lng, for GeoKit compatibility)
|
14
|
+
alias :lon :x
|
15
|
+
alias :lng :x
|
16
|
+
alias :lat :y
|
17
|
+
alias :rad :r
|
18
|
+
alias :tet :t
|
19
|
+
alias :tetha :t
|
20
|
+
|
21
|
+
def initialize(srid=DEFAULT_SRID,with_z=false,with_m=false)
|
22
|
+
super(srid,with_z,with_m)
|
23
|
+
@x = @y = 0.0
|
24
|
+
@z=0.0 #default value : meaningful if with_z
|
25
|
+
@m=0.0 #default value : meaningful if with_m
|
26
|
+
end
|
27
|
+
#sets all coordinates in one call. Use the +m+ accessor to set the m.
|
28
|
+
def set_x_y_z(x,y,z)
|
29
|
+
@x=x
|
30
|
+
@y=y
|
31
|
+
@z=z
|
32
|
+
self
|
33
|
+
end
|
34
|
+
alias :set_lon_lat_z :set_x_y_z
|
35
|
+
|
36
|
+
#sets all coordinates of a 2D point in one call
|
37
|
+
def set_x_y(x,y)
|
38
|
+
@x=x
|
39
|
+
@y=y
|
40
|
+
self
|
41
|
+
end
|
42
|
+
alias :set_lon_lat :set_x_y
|
43
|
+
|
44
|
+
#Return the distance between the 2D points (ie taking care only of the x and y coordinates), assuming
|
45
|
+
#the points are in projected coordinates. Euclidian distance in whatever unit the x and y ordinates are.
|
46
|
+
def euclidian_distance(point)
|
47
|
+
Math.sqrt((point.x - x)**2 + (point.y - y)**2)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Spherical distance in meters, using 'Haversine' formula.
|
51
|
+
# with a radius of 6471000m
|
52
|
+
# Assumes x is the lon and y the lat, in degrees (Changed in version 1.1).
|
53
|
+
# The user has to make sure using this distance makes sense (ie she should be in latlon coordinates)
|
54
|
+
def spherical_distance(point,r=6370997.0)
|
55
|
+
dlat = (point.lat - lat) * DEG2RAD / 2
|
56
|
+
dlon = (point.lon - lon) * DEG2RAD / 2
|
57
|
+
|
58
|
+
a = Math.sin(dlat)**2 + Math.cos(lat * DEG2RAD) * Math.cos(point.lat * DEG2RAD) * Math.sin(dlon)**2
|
59
|
+
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
|
60
|
+
r * c
|
61
|
+
end
|
62
|
+
|
63
|
+
#Ellipsoidal distance in m using Vincenty's formula. Lifted entirely from Chris Veness's code at http://www.movable-type.co.uk/scripts/LatLongVincenty.html and adapted for Ruby. Assumes the x and y are the lon and lat in degrees.
|
64
|
+
#a is the semi-major axis (equatorial radius) of the ellipsoid
|
65
|
+
#b is the semi-minor axis (polar radius) of the ellipsoid
|
66
|
+
#Their values by default are set to the ones of the WGS84 ellipsoid
|
67
|
+
def ellipsoidal_distance(point, a = 6378137.0, b = 6356752.3142)
|
68
|
+
f = (a-b) / a
|
69
|
+
l = (point.lon - lon) * DEG2RAD
|
70
|
+
|
71
|
+
u1 = Math.atan((1-f) * Math.tan(lat * DEG2RAD ))
|
72
|
+
u2 = Math.atan((1-f) * Math.tan(point.lat * DEG2RAD))
|
73
|
+
sinU1 = Math.sin(u1)
|
74
|
+
cosU1 = Math.cos(u1)
|
75
|
+
sinU2 = Math.sin(u2)
|
76
|
+
cosU2 = Math.cos(u2)
|
77
|
+
|
78
|
+
lambda = l
|
79
|
+
lambdaP = 2 * Math::PI
|
80
|
+
iterLimit = 20
|
81
|
+
|
82
|
+
while (lambda-lambdaP).abs > 1e-12 && --iterLimit>0
|
83
|
+
sinLambda = Math.sin(lambda)
|
84
|
+
cosLambda = Math.cos(lambda)
|
85
|
+
sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) + (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda))
|
86
|
+
|
87
|
+
return 0 if sinSigma == 0 #coincident points
|
88
|
+
|
89
|
+
cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda
|
90
|
+
sigma = Math.atan2(sinSigma, cosSigma)
|
91
|
+
sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma
|
92
|
+
cosSqAlpha = 1 - sinAlpha*sinAlpha
|
93
|
+
cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha
|
94
|
+
|
95
|
+
cos2SigmaM = 0 if (cos2SigmaM.nan?) #equatorial line: cosSqAlpha=0
|
96
|
+
|
97
|
+
c = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha))
|
98
|
+
lambdaP = lambda
|
99
|
+
lambda = l + (1-c) * f * sinAlpha * (sigma + c * sinSigma * (cos2SigmaM + c * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)))
|
100
|
+
end
|
101
|
+
return NaN if iterLimit==0 #formula failed to converge
|
102
|
+
|
103
|
+
uSq = cosSqAlpha * (a*a - b*b) / (b*b)
|
104
|
+
a_bis = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)))
|
105
|
+
b_bis = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)))
|
106
|
+
deltaSigma = b_bis * sinSigma*(cos2SigmaM + b_bis/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)- b_bis/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)))
|
107
|
+
|
108
|
+
b*a_bis*(sigma-deltaSigma)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Orthogonal Distance
|
112
|
+
# Based http://www.allegro.cc/forums/thread/589720
|
113
|
+
def orthogonal_distance(line, tail = nil)
|
114
|
+
head, tail = tail ? [line, tail] : [line[0], line[-1]]
|
115
|
+
a, b = @x - head.x, @y - head.y
|
116
|
+
c, d = tail.x - head.x, tail.y - head.y
|
117
|
+
|
118
|
+
dot = a * c + b * d
|
119
|
+
len = c * c + d * d
|
120
|
+
res = dot / len
|
121
|
+
|
122
|
+
xx, yy = if res < 0
|
123
|
+
[head.x, head.y]
|
124
|
+
elsif res > 1
|
125
|
+
[tail.x, tail.y]
|
126
|
+
else
|
127
|
+
[head.x + res * c, head.y + res * d]
|
128
|
+
end
|
129
|
+
# todo benchmark if worth creating an instance
|
130
|
+
# euclidian_distance(Point.from_x_y(xx, yy))
|
131
|
+
Math.sqrt((@x - xx) ** 2 + (@y - yy) ** 2)
|
132
|
+
end
|
133
|
+
|
134
|
+
#Bearing from a point to another, in degrees.
|
135
|
+
def bearing_to(other)
|
136
|
+
return 0 if self == other
|
137
|
+
a,b = other.x - self.x, other.y - self.y
|
138
|
+
res = Math.acos(b / Math.sqrt(a*a+b*b)) / Math::PI * 180;
|
139
|
+
a < 0 ? 360 - res : res
|
140
|
+
end
|
141
|
+
|
142
|
+
#Bearing from a point to another as symbols. (:n, :s, :sw, :ne...)
|
143
|
+
def bearing_text(other)
|
144
|
+
case bearing_to(other)
|
145
|
+
when 1..22 then :n
|
146
|
+
when 23..66 then :ne
|
147
|
+
when 67..112 then :e
|
148
|
+
when 113..146 then :se
|
149
|
+
when 147..202 then :s
|
150
|
+
when 203..246 then :sw
|
151
|
+
when 247..292 then :w
|
152
|
+
when 293..336 then :nw
|
153
|
+
when 337..360 then :n
|
154
|
+
else nil
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
#Bounding box in 2D/3D. Returns an array of 2 points
|
159
|
+
def bounding_box
|
160
|
+
unless with_z
|
161
|
+
[Point.from_x_y(@x,@y),Point.from_x_y(@x,@y)]
|
162
|
+
else
|
163
|
+
[Point.from_x_y_z(@x,@y,@z),Point.from_x_y_z(@x,@y,@z)]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def m_range
|
168
|
+
[@m,@m]
|
169
|
+
end
|
170
|
+
|
171
|
+
#tests the equality of the position of points + m
|
172
|
+
def ==(other)
|
173
|
+
return false unless other.kind_of?(Point)
|
174
|
+
@x == other.x and @y == other.y and @z == other.z and @m == other.m
|
175
|
+
end
|
176
|
+
|
177
|
+
#binary representation of a point. It lacks some headers to be a valid EWKB representation.
|
178
|
+
def binary_representation(allow_z=true,allow_m=true) #:nodoc:
|
179
|
+
bin_rep = [@x,@y].pack("EE")
|
180
|
+
bin_rep += [@z].pack("E") if @with_z and allow_z #Default value so no crash
|
181
|
+
bin_rep += [@m].pack("E") if @with_m and allow_m #idem
|
182
|
+
bin_rep
|
183
|
+
end
|
184
|
+
|
185
|
+
#WKB geometry type of a point
|
186
|
+
def binary_geometry_type#:nodoc:
|
187
|
+
1
|
188
|
+
end
|
189
|
+
|
190
|
+
#text representation of a point
|
191
|
+
def text_representation(allow_z=true,allow_m=true) #:nodoc:
|
192
|
+
tex_rep = "#{@x} #{@y}"
|
193
|
+
tex_rep += " #{@z}" if @with_z and allow_z
|
194
|
+
tex_rep += " #{@m}" if @with_m and allow_m
|
195
|
+
tex_rep
|
196
|
+
end
|
197
|
+
|
198
|
+
#WKT geometry type of a point
|
199
|
+
def text_geometry_type #:nodoc:
|
200
|
+
"POINT"
|
201
|
+
end
|
202
|
+
|
203
|
+
#georss simple representation
|
204
|
+
def georss_simple_representation(options) #:nodoc:
|
205
|
+
georss_ns = options[:georss_ns] || "georss"
|
206
|
+
geom_attr = options[:geom_attr]
|
207
|
+
"<#{georss_ns}:point#{geom_attr}>#{y} #{x}</#{georss_ns}:point>\n"
|
208
|
+
end
|
209
|
+
|
210
|
+
#georss w3c representation
|
211
|
+
def georss_w3cgeo_representation(options) #:nodoc:
|
212
|
+
w3cgeo_ns = options[:w3cgeo_ns] || "geo"
|
213
|
+
"<#{w3cgeo_ns}:lat>#{y}</#{w3cgeo_ns}:lat>\n<#{w3cgeo_ns}:long>#{x}</#{w3cgeo_ns}:long>\n"
|
214
|
+
end
|
215
|
+
|
216
|
+
#georss gml representation
|
217
|
+
def georss_gml_representation(options) #:nodoc:
|
218
|
+
georss_ns = options[:georss_ns] || "georss"
|
219
|
+
gml_ns = options[:gml_ns] || "gml"
|
220
|
+
result = "<#{georss_ns}:where>\n<#{gml_ns}:Point>\n<#{gml_ns}:pos>"
|
221
|
+
result += "#{y} #{x}"
|
222
|
+
result += "</#{gml_ns}:pos>\n</#{gml_ns}:Point>\n</#{georss_ns}:where>\n"
|
223
|
+
end
|
224
|
+
|
225
|
+
#outputs the geometry in kml format : options are <tt>:id</tt>, <tt>:tesselate</tt>, <tt>:extrude</tt>,
|
226
|
+
#<tt>:altitude_mode</tt>. If the altitude_mode option is not present, the Z (if present) will not be output (since
|
227
|
+
#it won't be used by GE anyway: clampToGround is the default)
|
228
|
+
def kml_representation(options = {}) #:nodoc:
|
229
|
+
result = "<Point#{options[:id_attr]}>\n"
|
230
|
+
result += options[:geom_data] if options[:geom_data]
|
231
|
+
result += "<coordinates>#{x},#{y}"
|
232
|
+
result += ",#{options[:fixed_z] || z ||0}" if options[:allow_z]
|
233
|
+
result += "</coordinates>\n"
|
234
|
+
result += "</Point>\n"
|
235
|
+
end
|
236
|
+
|
237
|
+
# Outputs the geometry in coordinates format:
|
238
|
+
# 47°52′48″, -20°06′00″
|
239
|
+
def as_latlong(opts = { })
|
240
|
+
val = []
|
241
|
+
[x,y].each_with_index do |l,i|
|
242
|
+
deg = l.to_i.abs
|
243
|
+
min = (60 * (l.abs - deg)).to_i
|
244
|
+
labs = (l * 1000000).abs / 1000000
|
245
|
+
sec = ((((labs - labs.to_i) * 60) - ((labs - labs.to_i) * 60).to_i) * 100000) * 60 / 100000
|
246
|
+
str = opts[:full] ? "%.i°%.2i′%05.2f″" : "%.i°%.2i′%02.0f″"
|
247
|
+
if opts[:coord]
|
248
|
+
out = str % [deg,min,sec]
|
249
|
+
if i == 0
|
250
|
+
out += l > 0 ? "N" : "S"
|
251
|
+
else
|
252
|
+
out += l > 0 ? "E" : "W"
|
253
|
+
end
|
254
|
+
val << out
|
255
|
+
else
|
256
|
+
val << str % [l.to_i, min, sec]
|
257
|
+
end
|
258
|
+
end
|
259
|
+
val.join(", ")
|
260
|
+
end
|
261
|
+
|
262
|
+
# Polar stuff
|
263
|
+
#
|
264
|
+
# http://www.engineeringtoolbox.com/converting-cartesian-polar-coordinates-d_1347.html
|
265
|
+
# http://rcoordinate.rubyforge.org/svn/point.rb
|
266
|
+
# outputs radium
|
267
|
+
def r; Math.sqrt(@x**2 + @y**2); end
|
268
|
+
|
269
|
+
# outputs theta
|
270
|
+
def theta_rad
|
271
|
+
if @x.zero?
|
272
|
+
@y < 0 ? 3 * HALFPI : HALFPI
|
273
|
+
else
|
274
|
+
th = Math.atan(@y/@x)
|
275
|
+
th += 2 * Math::PI if r > 0
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# outputs theta in degrees
|
280
|
+
def theta_deg; theta_rad / DEG2RAD; end
|
281
|
+
|
282
|
+
# outputs an array containing polar distance and theta
|
283
|
+
def as_polar; [r,t]; end
|
284
|
+
|
285
|
+
# invert signal of all coordinates
|
286
|
+
def -@
|
287
|
+
set_x_y_z(-@x, -@y, -@z)
|
288
|
+
end
|
289
|
+
|
290
|
+
#creates a point from an array of coordinates
|
291
|
+
def self.from_coordinates(coords,srid=DEFAULT_SRID,with_z=false,with_m=false)
|
292
|
+
if ! (with_z or with_m)
|
293
|
+
from_x_y(coords[0],coords[1],srid)
|
294
|
+
elsif with_z and with_m
|
295
|
+
from_x_y_z_m(coords[0],coords[1],coords[2],coords[3],srid)
|
296
|
+
elsif with_z
|
297
|
+
from_x_y_z(coords[0],coords[1],coords[2],srid)
|
298
|
+
else
|
299
|
+
from_x_y_m(coords[0],coords[1],coords[2],srid)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
#creates a point from the X and Y coordinates
|
304
|
+
def self.from_x_y(x,y,srid=DEFAULT_SRID)
|
305
|
+
point= new(srid)
|
306
|
+
point.set_x_y(x,y)
|
307
|
+
end
|
308
|
+
|
309
|
+
#creates a point from the X, Y and Z coordinates
|
310
|
+
def self.from_x_y_z(x,y,z,srid=DEFAULT_SRID)
|
311
|
+
point= new(srid,true)
|
312
|
+
point.set_x_y_z(x,y,z)
|
313
|
+
end
|
314
|
+
|
315
|
+
#creates a point from the X, Y and M coordinates
|
316
|
+
def self.from_x_y_m(x,y,m,srid=DEFAULT_SRID)
|
317
|
+
point= new(srid,false,true)
|
318
|
+
point.m=m
|
319
|
+
point.set_x_y(x,y)
|
320
|
+
end
|
321
|
+
|
322
|
+
#creates a point from the X, Y, Z and M coordinates
|
323
|
+
def self.from_x_y_z_m(x,y,z,m,srid=DEFAULT_SRID)
|
324
|
+
point= new(srid,true,true)
|
325
|
+
point.m=m
|
326
|
+
point.set_x_y_z(x,y,z)
|
327
|
+
end
|
328
|
+
|
329
|
+
#creates a point using polar coordinates
|
330
|
+
#r and theta(degrees)
|
331
|
+
def self.from_r_t(r,t,srid=DEFAULT_SRID)
|
332
|
+
t *= DEG2RAD
|
333
|
+
x = r * Math.cos(t)
|
334
|
+
y = r * Math.sin(t)
|
335
|
+
point= new(srid)
|
336
|
+
point.set_x_y(x,y)
|
337
|
+
end
|
338
|
+
|
339
|
+
#creates a point using coordinates like 22`34 23.45N
|
340
|
+
def self.from_latlong(lat,lon,srid=DEFAULT_SRID)
|
341
|
+
p = [lat,lon].map do |l|
|
342
|
+
sig, deg, min, sec, cen = l.scan(/(-)?(\d{1,2})\D*(\d{2})\D*(\d{2})(\D*(\d{1,3}))?/).flatten
|
343
|
+
sig = true if l =~ /W|S/
|
344
|
+
dec = deg.to_i + (min.to_i * 60 + "#{sec}#{cen}".to_f) / 3600
|
345
|
+
sig ? dec * -1 : dec
|
346
|
+
end
|
347
|
+
point= new(srid)
|
348
|
+
point.set_x_y(p[0],p[1])
|
349
|
+
end
|
350
|
+
|
351
|
+
#aliasing the constructors in case you want to use lat/lon instead of y/x
|
352
|
+
class << self
|
353
|
+
alias :xy :from_x_y
|
354
|
+
alias :xyz :from_x_y_z
|
355
|
+
alias :from_lon_lat_z :from_x_y_z
|
356
|
+
alias :from_lon_lat :from_x_y
|
357
|
+
alias :from_lon_lat_z :from_x_y_z
|
358
|
+
alias :from_lon_lat_m :from_x_y_m
|
359
|
+
alias :from_lon_lat_z_m :from_x_y_z_m
|
360
|
+
alias :from_rad_tet :from_r_t
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|