Class: GeoRuby::Base::Point

Inherits:
Geometry show all
Defined in:
lib/geo_ruby/base/point.rb

Overview

Represents a point. It is in 3D if the Z coordinate is not nil.

Constant Summary collapse

DEG2RAD =
0.0174532925199433

Instance Attribute Summary collapse

Attributes inherited from Geometry

#srid, #with_m, #with_z

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Geometry

#as_ewkb, #as_ewkt, #as_georss, #as_hex_ewkb, #as_hex_wkb, #as_kml, #as_wkb, #as_wkt, #envelope, from_ewkb, from_ewkt, from_georss, from_georss_with_tags, from_hex_ewkb, from_kml, kml_to_wkt

Constructor Details

#initialize(srid = @@default_srid, with_z = false, with_m = false) ⇒ Point

Returns a new instance of Point.



20
21
22
23
24
25
# File 'lib/geo_ruby/base/point.rb', line 20

def initialize(srid=@@default_srid,with_z=false,with_m=false)
  super(srid,with_z,with_m)
  @x = @y = 0.0
  @z=0.0 #default value : meaningful if with_z
  @m=0.0 #default value : meaningful if with_m
end

Instance Attribute Details

#mObject

Returns the value of attribute m.



9
10
11
# File 'lib/geo_ruby/base/point.rb', line 9

def m
  @m
end

#rObject (readonly)



10
11
12
# File 'lib/geo_ruby/base/point.rb', line 10

def r
  @r
end

#tObject (readonly) Also known as: tet, tetha

radium and theta



10
11
12
# File 'lib/geo_ruby/base/point.rb', line 10

def t
  @t
end

#xObject Also known as: lon, lng

Returns the value of attribute x.



9
10
11
# File 'lib/geo_ruby/base/point.rb', line 9

def x
  @x
end

#yObject Also known as: lat

Returns the value of attribute y.



9
10
11
# File 'lib/geo_ruby/base/point.rb', line 9

def y
  @y
end

#zObject

Returns the value of attribute z.



9
10
11
# File 'lib/geo_ruby/base/point.rb', line 9

def z
  @z
end

Class Method Details

.from_coordinates(coords, srid = @@default_srid, with_z = false, with_m = false) ⇒ Object

creates a point from an array of coordinates



240
241
242
243
244
245
246
247
248
249
250
# File 'lib/geo_ruby/base/point.rb', line 240

def self.from_coordinates(coords,srid=@@default_srid,with_z=false,with_m=false)
  if ! (with_z or with_m)
    from_x_y(coords[0],coords[1],srid)
  elsif with_z and with_m
    from_x_y_z_m(coords[0],coords[1],coords[2],coords[3],srid)
  elsif with_z
    from_x_y_z(coords[0],coords[1],coords[2],srid)
  else
    from_x_y_m(coords[0],coords[1],coords[2],srid)
  end
end

.from_latlong(lat, lon, srid = @@default_srid) ⇒ Object

creates a point using coordinates like 22`34 23.45N



289
290
291
292
293
294
295
296
297
298
# File 'lib/geo_ruby/base/point.rb', line 289

def self.from_latlong(lat,lon,srid=@@default_srid)
  p = [lat,lon].map do |l|
    sig, deg, min, sec, cen = l.scan(/(-)?(\d{1,2})\D*(\d{2})\D*(\d{2})(\D*(\d{1,3}))?/).flatten
    sig = true if l =~ /W|S/
    dec = deg.to_i + (min.to_i * 60 + "#{sec}#{cen}".to_f) / 3600
    sig ? dec * -1 : dec
  end
  point= new(srid)
  point.set_x_y(p[0],p[1])
end

.from_r_t(r, t, srid = @@default_srid) ⇒ Object Also known as: from_rad_tet

creates a point using polar coordinates r and theta(degrees)



280
281
282
283
284
285
286
# File 'lib/geo_ruby/base/point.rb', line 280

def self.from_r_t(r,t,srid=@@default_srid)
  t *= DEG2RAD
  x = r * Math.cos(t)
  y = r * Math.sin(t)
  point= new(srid)
  point.set_x_y(x,y)
end

.from_x_y(x, y, srid = @@default_srid) ⇒ Object Also known as: from_lon_lat

creates a point from the X and Y coordinates



253
254
255
256
# File 'lib/geo_ruby/base/point.rb', line 253

def self.from_x_y(x,y,srid=@@default_srid)
  point= new(srid)
  point.set_x_y(x,y)
end

.from_x_y_m(x, y, m, srid = @@default_srid) ⇒ Object Also known as: from_lon_lat_m

creates a point from the X, Y and M coordinates



265
266
267
268
269
# File 'lib/geo_ruby/base/point.rb', line 265

def self.from_x_y_m(x,y,m,srid=@@default_srid)
  point= new(srid,false,true)
  point.m=m
  point.set_x_y(x,y)
end

.from_x_y_z(x, y, z, srid = @@default_srid) ⇒ Object Also known as: from_lon_lat_z

creates a point from the X, Y and Z coordinates



259
260
261
262
# File 'lib/geo_ruby/base/point.rb', line 259

def self.from_x_y_z(x,y,z,srid=@@default_srid)
  point= new(srid,true)
  point.set_x_y_z(x,y,z)
end

.from_x_y_z_m(x, y, z, m, srid = @@default_srid) ⇒ Object Also known as: from_lon_lat_z_m

creates a point from the X, Y, Z and M coordinates



272
273
274
275
276
# File 'lib/geo_ruby/base/point.rb', line 272

def self.from_x_y_z_m(x,y,z,m,srid=@@default_srid)
  point= new(srid,true,true)
  point.m=m
  point.set_x_y_z(x,y,z)
end

Instance Method Details

#==(other_point) ⇒ Object

tests the equality of the position of points + m



125
126
127
128
129
130
131
# File 'lib/geo_ruby/base/point.rb', line 125

def ==(other_point)
  if other_point.class != self.class
    false
  else
    @x == other_point.x and @y == other_point.y and @z == other_point.z and @m == other_point.m
  end
end

#as_latlong(opts = { }) ⇒ Object

Outputs the geometry in coordinates format: 47°52′48″, -20°06′00″



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/geo_ruby/base/point.rb', line 191

def as_latlong(opts = { })
  val = []
  [x,y].each_with_index do |l,i|
    deg = l.to_i.abs
    min = (60 * (l.abs - deg)).to_i
    labs = (l * 1000000).abs / 1000000
    sec = ((((labs - labs.to_i) * 60) - ((labs - labs.to_i) * 60).to_i) * 100000) * 60 / 100000
    str = opts[:full] ? "%.i°%.2i′%05.2f″" :  "%.i°%.2i′%02.0f″"
    if opts[:coord]
      out = str % [deg,min,sec]
      if i == 0
        out += l > 0 ? "N" : "S"
      else
        out += l > 0 ? "E" : "W"
      end
      val << out
    else
      val << str % [l.to_i, min, sec]
    end
  end
  val.join(", ")
end

#as_polarObject

outputs an array containing polar distance and theta



235
236
237
# File 'lib/geo_ruby/base/point.rb', line 235

def as_polar
  [r,t]
end

#binary_geometry_typeObject

WKB geometry type of a point



141
142
143
# File 'lib/geo_ruby/base/point.rb', line 141

def binary_geometry_type#:nodoc:
  1
end

#binary_representation(allow_z = true, allow_m = true) ⇒ Object

binary representation of a point. It lacks some headers to be a valid EWKB representation.



134
135
136
137
138
139
# File 'lib/geo_ruby/base/point.rb', line 134

def binary_representation(allow_z=true,allow_m=true) #:nodoc:
  bin_rep = [@x,@y].pack("EE")
  bin_rep += [@z].pack("E") if @with_z and allow_z #Default value so no crash
  bin_rep += [@m].pack("E") if @with_m and allow_m #idem
  bin_rep
end

#bounding_boxObject

Bounding box in 2D/3D. Returns an array of 2 points



112
113
114
115
116
117
118
# File 'lib/geo_ruby/base/point.rb', line 112

def bounding_box
  unless with_z
    [Point.from_x_y(@x,@y),Point.from_x_y(@x,@y)]
  else
    [Point.from_x_y_z(@x,@y,@z),Point.from_x_y_z(@x,@y,@z)]
  end
end

#ellipsoidal_distance(point, a = 6378137.0, b = 6356752.3142) ⇒ Object

Ellipsoidal distance in m using Vincenty’s formula. Lifted entirely from Chris Veness’s code at www.movable-type.co.uk/scripts/LatLongVincenty.html and adapted for Ruby. Assumes the x and y are the lon and lat in degrees. a is the semi-major axis (equatorial radius) of the ellipsoid b is the semi-minor axis (polar radius) of the ellipsoid Their values by default are set to the ones of the WGS84 ellipsoid



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/geo_ruby/base/point.rb', line 67

def ellipsoidal_distance(point, a = 6378137.0, b = 6356752.3142)
  f = (a-b) / a
  l = (point.lon - lon) * DEG2RAD

  u1 = Math.atan((1-f) * Math.tan(lat * DEG2RAD ))
  u2 = Math.atan((1-f) * Math.tan(point.lat * DEG2RAD))
  sinU1 = Math.sin(u1)
  cosU1 = Math.cos(u1)
  sinU2 = Math.sin(u2)
  cosU2 = Math.cos(u2)

  lambda = l
  lambdaP = 2 * Math::PI
  iterLimit = 20

  while (lambda-lambdaP).abs > 1e-12 && --iterLimit>0
    sinLambda = Math.sin(lambda)
    cosLambda = Math.cos(lambda)
    sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) + (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda))

    return 0 if sinSigma == 0 #coincident points

    cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda
    sigma = Math.atan2(sinSigma, cosSigma)
    sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma
    cosSqAlpha = 1 - sinAlpha*sinAlpha
    cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha

    cos2SigmaM = 0 if (cos2SigmaM.nan?) #equatorial line: cosSqAlpha=0

    c = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha))
    lambdaP = lambda
    lambda = l + (1-c) * f * sinAlpha * (sigma + c * sinSigma * (cos2SigmaM + c * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)))
  end
  return NaN if iterLimit==0 #formula failed to converge

  uSq = cosSqAlpha * (a*a - b*b) / (b*b)
  a_bis = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)))
  b_bis = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)))
  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)))

  b*a_bis*(sigma-deltaSigma)
end

#euclidian_distance(point) ⇒ Object

Return the distance between the 2D points (ie taking care only of the x and y coordinates), assuming the points are in projected coordinates. Euclidian distance in whatever unit the x and y ordinates are.



45
46
47
# File 'lib/geo_ruby/base/point.rb', line 45

def euclidian_distance(point)
  Math.sqrt((point.x - x)**2 + (point.y - y)**2)
end

#georss_gml_representation(options) ⇒ Object

georss gml representation



169
170
171
172
173
174
175
# File 'lib/geo_ruby/base/point.rb', line 169

def georss_gml_representation(options) #:nodoc:
  georss_ns = options[:georss_ns] || "georss"
  gml_ns = options[:gml_ns] || "gml"
  result = "<#{georss_ns}:where>\n<#{gml_ns}:Point>\n<#{gml_ns}:pos>"
  result += "#{y} #{x}"
  result += "</#{gml_ns}:pos>\n</#{gml_ns}:Point>\n</#{georss_ns}:where>\n"
end

#georss_simple_representation(options) ⇒ Object

georss simple representation



158
159
160
161
162
# File 'lib/geo_ruby/base/point.rb', line 158

def georss_simple_representation(options) #:nodoc:
  georss_ns = options[:georss_ns] || "georss"
  geom_attr = options[:geom_attr]
  "<#{georss_ns}:point#{geom_attr}>#{y} #{x}</#{georss_ns}:point>\n"
end

#georss_w3cgeo_representation(options) ⇒ Object

georss w3c representation



164
165
166
167
# File 'lib/geo_ruby/base/point.rb', line 164

def georss_w3cgeo_representation(options) #:nodoc:
  w3cgeo_ns = options[:w3cgeo_ns] || "geo"
  "<#{w3cgeo_ns}:lat>#{y}</#{w3cgeo_ns}:lat>\n<#{w3cgeo_ns}:long>#{x}</#{w3cgeo_ns}:long>\n"
end

#kml_representation(options = {}) ⇒ Object

outputs the geometry in kml format : options are :id, :tesselate, :extrude, :altitude_mode. If the altitude_mode option is not present, the Z (if present) will not be output (since it won’t be used by GE anyway: clampToGround is the default)



180
181
182
183
184
185
186
187
# File 'lib/geo_ruby/base/point.rb', line 180

def kml_representation(options = {}) #:nodoc:
  result = "<Point#{options[:id_attr]}>\n"
  result += options[:geom_data] if options[:geom_data]
  result += "<coordinates>#{x},#{y}"
  result += ",#{options[:fixed_z] || z ||0}" if options[:allow_z]
  result += "</coordinates>\n"
  result += "</Point>\n"
end

#m_rangeObject



120
121
122
# File 'lib/geo_ruby/base/point.rb', line 120

def m_range
  [@m,@m]
end

#radObject

radium and theta



16
17
18
# File 'lib/geo_ruby/base/point.rb', line 16

def r
  @r
end

#set_x_y(x, y) ⇒ Object Also known as: set_lon_lat

sets all coordinates of a 2D point in one call



36
37
38
39
40
# File 'lib/geo_ruby/base/point.rb', line 36

def set_x_y(x,y)
  @x=x
  @y=y
  self
end

#set_x_y_z(x, y, z) ⇒ Object Also known as: set_lon_lat_z

sets all coordinates in one call. Use the m accessor to set the m.



27
28
29
30
31
32
# File 'lib/geo_ruby/base/point.rb', line 27

def set_x_y_z(x,y,z)
  @x=x
  @y=y
  @z=z
  self
end

#spherical_distance(point, r = 6370997.0) ⇒ Object

Returns the sperical distance in meters, with a radius of 6471000m, with the haversine law. Assumes x is the lon and y the lat, in degrees (Changed in version 1.1). The user has to make sure using this distance makes sense (ie she should be in latlon coordinates)



52
53
54
55
56
57
58
59
60
61
# File 'lib/geo_ruby/base/point.rb', line 52

def spherical_distance(point,r=6370997.0)
  radlat_from = lat * DEG2RAD
  radlat_to = point.lat * DEG2RAD
  dlat = (point.lat - lat) * DEG2RAD / 2
  dlon = (point.lon - lon) * DEG2RAD / 2

  a = Math.sin(dlat)**2 + Math.cos(radlat_from) * Math.cos(radlat_to) * Math.sin(dlon)**2
  c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
  r * c
end

#text_geometry_typeObject

WKT geometry type of a point



153
154
155
# File 'lib/geo_ruby/base/point.rb', line 153

def text_geometry_type #:nodoc:
  "POINT"
end

#text_representation(allow_z = true, allow_m = true) ⇒ Object

text representation of a point



146
147
148
149
150
151
# File 'lib/geo_ruby/base/point.rb', line 146

def text_representation(allow_z=true,allow_m=true) #:nodoc:
  tex_rep = "#{@x} #{@y}"
  tex_rep += " #{@z}" if @with_z and allow_z
  tex_rep += " #{@m}" if @with_m and allow_m
  tex_rep
end

#theta_degObject



230
231
232
# File 'lib/geo_ruby/base/point.rb', line 230

def theta_deg
  theta_rad / DEG2RAD
end

#theta_radObject

outputs theta



221
222
223
224
225
226
227
228
# File 'lib/geo_ruby/base/point.rb', line 221

def theta_rad
  if @x.zero?
    @y < 0 ? 3 * Math::PI / 2 : Math::PI / 2
  else
    th = Math.atan(@y/@x)
    th += 2 * Math::PI if r > 0
  end
end