Class: GeoRuby::SimpleFeatures::Point

Inherits:
Geometry
  • Object
show all
Defined in:
lib/geo_ruby/simple_features/point.rb

Overview

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

Constant Summary collapse

DEG2RAD =
0.0174532925199433
HALFPI =
1.5707963267948966

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.


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

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


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

def m
  @m
end

#rObject (readonly)


11
12
13
# File 'lib/geo_ruby/simple_features/point.rb', line 11

def r
  @r
end

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

radium and theta


11
12
13
# File 'lib/geo_ruby/simple_features/point.rb', line 11

def t
  @t
end

#xObject Also known as: lon, lng

Returns the value of attribute x


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

def x
  @x
end

#yObject Also known as: lat

Returns the value of attribute y


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

def y
  @y
end

#zObject

Returns the value of attribute z


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

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


291
292
293
294
295
296
297
298
299
300
301
# File 'lib/geo_ruby/simple_features/point.rb', line 291

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


340
341
342
343
344
345
346
347
348
349
# File 'lib/geo_ruby/simple_features/point.rb', line 340

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)


331
332
333
334
335
336
337
# File 'lib/geo_ruby/simple_features/point.rb', line 331

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: xy, from_lon_lat

creates a point from the X and Y coordinates


304
305
306
307
# File 'lib/geo_ruby/simple_features/point.rb', line 304

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


316
317
318
319
320
# File 'lib/geo_ruby/simple_features/point.rb', line 316

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: xyz, from_lon_lat_z

creates a point from the X, Y and Z coordinates


310
311
312
313
# File 'lib/geo_ruby/simple_features/point.rb', line 310

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


323
324
325
326
327
# File 'lib/geo_ruby/simple_features/point.rb', line 323

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

#[email protected]Object

invert signal of all coordinates


286
287
288
# File 'lib/geo_ruby/simple_features/point.rb', line 286

def [email protected]
  set_x_y_z(-@x, -@y, -@z)
end

#==(other) ⇒ Object

tests the equality of the position of points + m


172
173
174
175
# File 'lib/geo_ruby/simple_features/point.rb', line 172

def ==(other)
  return false unless other.kind_of?(Point)
  @x == other.x and @y == other.y and @z == other.z and @m == other.m
end

#as_latlong(opts = { }) ⇒ Object

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


239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/geo_ruby/simple_features/point.rb', line 239

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


283
# File 'lib/geo_ruby/simple_features/point.rb', line 283

def as_polar;        [r,t];      end

#bearing_text(other) ⇒ Object

Bearing from a point to another as symbols. (:n, :s, :sw, :ne…)


143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/geo_ruby/simple_features/point.rb', line 143

def bearing_text(other)
  case bearing_to(other)
  when 1..22    then :n
  when 23..66   then :ne
  when 67..112  then :e
  when 113..146 then :se
  when 147..202 then :s
  when 203..246 then :sw
  when 247..292 then :w
  when 293..336 then :nw
  when 337..360 then :n
  else nil
  end
end

#bearing_to(other) ⇒ Object

Bearing from a point to another, in degrees.


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

def bearing_to(other)
  return 0 if self == other
  a,b =  other.x - self.x, other.y - self.y
  res =  Math.acos(b / Math.sqrt(a*a+b*b)) / Math::PI * 180;
  a < 0 ? 360 - res : res
end

#binary_geometry_typeObject

WKB geometry type of a point


186
187
188
# File 'lib/geo_ruby/simple_features/point.rb', line 186

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.


178
179
180
181
182
183
# File 'lib/geo_ruby/simple_features/point.rb', line 178

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


159
160
161
162
163
164
165
# File 'lib/geo_ruby/simple_features/point.rb', line 159

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/simple_features/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.


46
47
48
# File 'lib/geo_ruby/simple_features/point.rb', line 46

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

#georss_gml_representation(options) ⇒ Object

georss gml representation


217
218
219
220
221
222
223
# File 'lib/geo_ruby/simple_features/point.rb', line 217

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


204
205
206
207
208
# File 'lib/geo_ruby/simple_features/point.rb', line 204

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


211
212
213
214
# File 'lib/geo_ruby/simple_features/point.rb', line 211

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)


228
229
230
231
232
233
234
235
# File 'lib/geo_ruby/simple_features/point.rb', line 228

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


167
168
169
# File 'lib/geo_ruby/simple_features/point.rb', line 167

def m_range
  [@m,@m]
end

#orthogonal_distance(line, tail = nil) ⇒ Object

Orthogonal Distance Based www.allegro.cc/forums/thread/589720


113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/geo_ruby/simple_features/point.rb', line 113

def orthogonal_distance(line, tail = nil)
  head, tail  = tail ?  [line, tail] : [line[0], line[-1]]
  a, b = @x - head.x, @y - head.y
  c, d = tail.x - head.x, tail.y - head.y

  dot = a * c + b * d
  len = c * c + d * d
  res = dot / len

  xx, yy = if res < 0
             [head.x, head.y]
           elsif res > 1
             [tail.x, tail.y]
           else
             [head.x + res * c, head.y + res * d]
           end
  # todo benchmark if worth creating an instance
  # euclidian_distance(Point.from_x_y(xx, yy))
  Math.sqrt((@x - xx) ** 2 + (@y - yy) ** 2)
end

#radObject

radium and theta


17
18
19
# File 'lib/geo_ruby/simple_features/point.rb', line 17

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


37
38
39
40
41
# File 'lib/geo_ruby/simple_features/point.rb', line 37

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.


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

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

#spherical_distance(point, r = 6370997.0) ⇒ Object

Spherical distance in meters, using 'Haversine' formula. with a radius of 6471000m 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)


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

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

  a = Math.sin(dlat)**2 + Math.cos(lat * DEG2RAD) * Math.cos(point.lat * DEG2RAD) * 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


199
200
201
# File 'lib/geo_ruby/simple_features/point.rb', line 199

def text_geometry_type #:nodoc:
  "POINT"
end

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

text representation of a point


191
192
193
194
195
196
# File 'lib/geo_ruby/simple_features/point.rb', line 191

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

outputs theta in degrees


280
# File 'lib/geo_ruby/simple_features/point.rb', line 280

def theta_deg;        theta_rad / DEG2RAD;      end

#theta_radObject

outputs theta


270
271
272
273
274
275
276
277
# File 'lib/geo_ruby/simple_features/point.rb', line 270

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