class GeoRuby::SimpleFeatures::Point

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

Constants

DEG2RAD
HALFPI

Attributes

lat[RW]
lng[RW]
lon[RW]
m[RW]
r[R]
rad[R]
t[R]
tet[R]
tetha[R]
x[RW]
y[RW]
z[RW]

Public Class Methods

from_coordinates(coords, srid = DEFAULT_SRID, z = false, m = false) click to toggle source

Creates a point from an array of coordinates

# File lib/geo_ruby/simple_features/point.rb, line 372
def self.from_coordinates(coords, srid = DEFAULT_SRID, z = false, m = false)
  if !(z || m)
    from_x_y(coords[0], coords[1], srid)
  elsif z && m
    from_x_y_z_m(coords[0], coords[1], coords[2], coords[3], srid)
  elsif 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) click to toggle source

Creates a point using coordinates like 22`34 23.45N

# File lib/geo_ruby/simple_features/point.rb, line 421
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_lon_lat(x, y, srid = DEFAULT_SRID)
Alias for: from_x_y
from_lon_lat_m(x, y, m, srid = DEFAULT_SRID)
Alias for: from_x_y_m
from_lon_lat_z(x, y, z, srid = DEFAULT_SRID)
Alias for: from_x_y_z
from_lon_lat_z_m(x, y, z, m, srid = DEFAULT_SRID)
Alias for: from_x_y_z_m
from_r_t(r, t, srid = DEFAULT_SRID) click to toggle source

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

# File lib/geo_ruby/simple_features/point.rb, line 412
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
Also aliased as: from_rad_tet
from_rad_tet(r, t, srid = DEFAULT_SRID)
Alias for: from_r_t
from_x_y(x, y, srid = DEFAULT_SRID) click to toggle source

Creates a point from the X and Y coordinates

# File lib/geo_ruby/simple_features/point.rb, line 385
def self.from_x_y(x, y, srid = DEFAULT_SRID)
  point = new(srid)
  point.set_x_y(x, y)
end
Also aliased as: xy, from_xy, from_lon_lat
from_x_y_m(x, y, m, srid = DEFAULT_SRID) click to toggle source

Creates a point from the X, Y and M coordinates

# File lib/geo_ruby/simple_features/point.rb, line 397
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
Also aliased as: from_lon_lat_m
from_x_y_z(x, y, z, srid = DEFAULT_SRID) click to toggle source

Creates a point from the X, Y and Z coordinates

# File lib/geo_ruby/simple_features/point.rb, line 391
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
Also aliased as: xyz, from_xyz, from_lon_lat_z, from_lon_lat_z
from_x_y_z_m(x, y, z, m, srid = DEFAULT_SRID) click to toggle source

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

# File lib/geo_ruby/simple_features/point.rb, line 404
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
Also aliased as: from_lon_lat_z_m
from_xy(x, y, srid = DEFAULT_SRID)
Alias for: from_x_y
from_xyz(x, y, z, srid = DEFAULT_SRID)
Alias for: from_x_y_z
new(srid = DEFAULT_SRID, with_z = false, with_m = false) click to toggle source
Calls superclass method GeoRuby::SimpleFeatures::Geometry::new
# 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
xy(x, y, srid = DEFAULT_SRID)
Alias for: from_x_y
xyz(x, y, z, srid = DEFAULT_SRID)
Alias for: from_x_y_z

Public Instance Methods

-@() click to toggle source

Invert signal of all coordinates

# File lib/geo_ruby/simple_features/point.rb, line 349
def -@
  set_x_y_z(-@x, -@y, -@z)
end
==(other) click to toggle source

Tests the equality of the position of points + m

# File lib/geo_ruby/simple_features/point.rb, line 198
def ==(other)
  return false unless other.is_a?(Point)
  @x == other.x && @y == other.y && @z == other.z && @m == other.m
end
as_json(_options = {}) click to toggle source

Outputs the point in json format

# File lib/geo_ruby/simple_features/point.rb, line 344
def as_json(_options = {})
  { type: 'Point', coordinates: to_coordinates }
end
as_lat(options = {}) click to toggle source

Outputs the geometry coordinate in human format: 47°52′48″N

# File lib/geo_ruby/simple_features/point.rb, line 296
def as_lat(options = {})
  human_representation(options, x: x).join
end
as_latlong(options = {}) click to toggle source

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

# File lib/geo_ruby/simple_features/point.rb, line 309
def as_latlong(options = {})
  human_representation(options).join(', ')
end
Also aliased as: as_ll
as_ll(options = {})
Alias for: as_latlong
as_lng(options = {})
Alias for: as_long
as_long(options = {}) click to toggle source

Outputs the geometry coordinate in human format: -20°06′00W″

# File lib/geo_ruby/simple_features/point.rb, line 302
def as_long(options = {})
  human_representation(options, y: y).join
end
Also aliased as: as_lng
as_polar() click to toggle source

Outputs an array containing polar distance and theta

# File lib/geo_ruby/simple_features/point.rb, line 339
def as_polar
  [r, t]
end
bearing_text(other) click to toggle source

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

# File lib/geo_ruby/simple_features/point.rb, line 169
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) click to toggle source

Bearing from a point to another, in degrees.

# File lib/geo_ruby/simple_features/point.rb, line 161
def bearing_to(other)
  return 0 if self == other
  a, b =  other.x - x, other.y - y
  res =  Math.acos(b / Math.sqrt(a * a + b * b)) / Math::PI * 180
  a < 0 ? 360 - res : res
end
bounding_box() click to toggle source

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

# File lib/geo_ruby/simple_features/point.rb, line 185
def bounding_box
  if with_z
    [Point.from_x_y_z(@x, @y, @z), Point.from_x_y_z(@x, @y, @z)]
  else
    [Point.from_x_y(@x, @y), Point.from_x_y(@x, @y)]
  end
end
ellipsoidal_distance(point, a = 6_378_137.0, b = 6_356_752.3142) click to toggle source

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 WGS84 ellipsoid.

# File lib/geo_ruby/simple_features/point.rb, line 81
def ellipsoidal_distance(point, a = 6_378_137.0, b = 6_356_752.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))
  sin_u1 = Math.sin(u1)
  cos_u1 = Math.cos(u1)
  sin_u2 = Math.sin(u2)
  cos_u2 = Math.cos(u2)

  lambda = l
  lambda_p = 2 * Math::PI
  iter_limit = 20

  while (lambda - lambda_p).abs > 1e-12 && --iter_limit > 0
    sin_lambda = Math.sin(lambda)
    cos_lambda = Math.cos(lambda)
    sin_sigma = \
    Math.sqrt((cos_u2 * sin_lambda) * (cos_u2 * sin_lambda) +
              (cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda) *
              (cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda))

    return 0 if sin_sigma == 0 # coincident points

    cos_sigma   = sin_u1 * sin_u2 + cos_u1 * cos_u2 * cos_lambda
    sigma      = Math.atan2(sin_sigma, cos_sigma)
    sin_alpha   = cos_u1 * cos_u2 * sin_lambda / sin_sigma
    cos_sq_alpha = 1 - sin_alpha * sin_alpha
    cos2_sigma_m = cos_sigma - 2 * sin_u1 * sin_u2 / cos_sq_alpha

    # equatorial line: cos_sq_alpha=0
    cos2_sigma_m = 0 if cos2_sigma_m.nan?

    c = f / 16 * cos_sq_alpha * (4 + f * (4 - 3 * cos_sq_alpha))
    lambda_p = lambda
    lambda = l + (1 - c) * f * sin_alpha * (sigma + c * sin_sigma *
      (cos2_sigma_m + c * cos_sigma * (-1 + 2 * cos2_sigma_m *
          cos2_sigma_m)))
  end

  return NaN if iter_limit == 0 # formula failed to converge

  usq = cos_sq_alpha * (a * a - b * b) / (b * b)
  a_bis = 1 + usq / 16_384 * (4096 + usq * (-768 + usq * (320 - 175 * usq)))
  b_bis = usq / 1024 * (256 + usq * (-128 + usq * (74 - 47 * usq)))
  delta_sigma = b_bis * sin_sigma * (cos2_sigma_m + b_bis / 4 *
    (cos_sigma * (-1 + 2 * cos2_sigma_m * cos2_sigma_m) - b_bis / 6 *
      cos2_sigma_m * (-3 + 4 * sin_sigma * sin_sigma) * (-3 + 4 *
        cos2_sigma_m * cos2_sigma_m)))

  b * a_bis * (sigma - delta_sigma)
end
euclidian_distance(point) click to toggle source

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.

# File lib/geo_ruby/simple_features/point.rb, line 51
def euclidian_distance(point)
  Math.sqrt((point.x - x)**2 + (point.y - y)**2)
end
html_representation(options = {}) click to toggle source
# File lib/geo_ruby/simple_features/point.rb, line 266
def html_representation(options = {})
  options[:coord] = true if options[:coord].nil?
  out =  '<span class=\'geo\'>'
  out += "<abbr class='latitude' title='#{x}'>#{as_lat(options)}</abbr>"
  out += "<abbr class='longitude' title='#{y}'>#{as_long(options)}</abbr>"
  out + '</span>'
end
human_representation(options = {}, g = { x: x, y: y }) click to toggle source

Human representation of the geom, don’t use directly, use: as_lat, as_long, as_latlong

# File lib/geo_ruby/simple_features/point.rb, line 276
def human_representation(options = {}, g = { x: x, y: y })
  g.map do |k, v|
    deg = v.to_i.abs
    min = (60 * (v.abs - deg)).to_i
    labs = (v * 1_000_000).abs / 1_000_000
    sec = ((((labs - labs.to_i) * 60) -
        ((labs - labs.to_i) * 60).to_i) * 100_000) * 60 / 100_000
    str = options[:full] ? '%.i°%.2i′%05.2f″' :  '%.i°%.2i′%02.0f″'
    if options[:coord]
      out = format(str, deg, min, sec)
      # Add cardinal
      out + (k == :x ? v > 0 ? 'N' : 'S' : v > 0 ? 'E' : 'W')
    else
      format(str, v.to_i, min, sec)
    end
  end
end
m_range() click to toggle source
# File lib/geo_ruby/simple_features/point.rb, line 193
def m_range
  [@m, @m]
end
orthogonal_distance(line, tail = nil) click to toggle source

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

# File lib/geo_ruby/simple_features/point.rb, line 137
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
  return 0.0 if len.zero?
  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
set_lon_lat(x, y)
Alias for: set_x_y
set_lon_lat_z(x, y, z)
Alias for: set_x_y_z
set_x_y(x, y) click to toggle source

Sets all coordinates of a 2D point in one call

# File lib/geo_ruby/simple_features/point.rb, line 39
def set_x_y(x, y)
  @x = x && !x.is_a?(Numeric) ? x.to_f : x
  @y = y && !y.is_a?(Numeric) ? y.to_f : y
  self
end
Also aliased as: set_lon_lat
set_x_y_z(x, y, z) click to toggle source

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

# File lib/geo_ruby/simple_features/point.rb, line 30
def set_x_y_z(x, y, z)
  @x = x && !x.is_a?(Numeric) ? x.to_f : x
  @y = y && !y.is_a?(Numeric) ? y.to_f : y
  @z = z && !z.is_a?(Numeric) ? z.to_f : z
  self
end
Also aliased as: set_lon_lat_z
spherical_distance(point, r = 6_370_997.0) click to toggle source

Spherical distance in meters, using ‘Haversine’ formula. with a radius of 6471000m Assumes x is the lon and y the lat, in degrees. The user has to make sure using this distance makes sense (ie she should be in latlon coordinates)

# File lib/geo_ruby/simple_features/point.rb, line 60
def spherical_distance(point, r = 6_370_997.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
theta_deg() click to toggle source

Outputs theta in degrees

# File lib/geo_ruby/simple_features/point.rb, line 334
def theta_deg
  theta_rad / DEG2RAD
end
theta_rad() click to toggle source

Outputs theta

# File lib/geo_ruby/simple_features/point.rb, line 324
def theta_rad
  if @x.zero?
    @y < 0 ? 3 * HALFPI : HALFPI
  else
    th = Math.atan(@y / @x)
    r > 0 ? th + 2 * Math::PI : th
  end
end
to_coordinates() click to toggle source

Helper to get all coordinates as array.

# File lib/geo_ruby/simple_features/point.rb, line 354
def to_coordinates
  coord = [x, y]
  coord << z if with_z
  coord << m if with_m
  coord
end
to_xy() click to toggle source

Simple helper for 2D maps

# File lib/geo_ruby/simple_features/point.rb, line 362
def to_xy
  [x, y]
end
to_xyz() click to toggle source

Simple helper for 3D maps

# File lib/geo_ruby/simple_features/point.rb, line 367
def to_xyz
  [x, y, z]
end