Class: NSWTopo::GeoJSON::LineString

Inherits:
Object
  • Object
show all
Includes:
SVG
Defined in:
lib/nswtopo/gis/geojson/line_string.rb

Direct Known Subclasses

Labels::ConvexHull

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.[](coordinates, properties = nil, &block) ⇒ Object



8
9
10
11
12
13
14
15
16
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 8

def self.[](coordinates, properties = nil, &block)
  new(coordinates, properties) do
    sanitised = @coordinates.map do |point|
      Vector === point ? point : Vector[*point]
    end.chunk(&:itself).map(&:first)
    @coordinates.replace sanitised
    block.call self if block_given?
  end
end

Instance Method Details

#anticlockwise?Boolean Also known as: exterior?

Returns:

  • (Boolean)


43
44
45
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 43

def anticlockwise?
  signed_area >= 0
end

#boundsObject



18
19
20
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 18

def bounds
  @coordinates.transpose.map(&:minmax)
end

#clockwise?Boolean Also known as: interior?

Returns:

  • (Boolean)


38
39
40
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 38

def clockwise?
  signed_area < 0
end

#closed?Boolean

Returns:

  • (Boolean)


30
31
32
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 30

def closed?
  @coordinates.last == @coordinates.first
end

#crop(length) ⇒ Object



117
118
119
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 117

def crop(length)
  trim((path_length - length) / 2)
end

#path_lengthObject



26
27
28
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 26

def path_length
  each_cons(2).sum { |p0, p1| (p1 - p0).norm }
end

#reverseObject



22
23
24
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 22

def reverse
  LineString.new @coordinates.reverse, @properties
end

#sample_at(interval, offset: 0, &block) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 65

def sample_at(interval, offset: 0, &block)
  Enumerator.new do |yielder|
    alpha = (0.5 + Float(offset || 0) / interval) % 1.0
    each_cons(2).inject [alpha, 0] do |(alpha, along), (p0, p1)|
      angle = (p1 - p0).angle
      loop do
        distance = (p1 - p0).norm
        fraction = alpha * interval / distance
        break unless fraction < 1
        p0 = p1 * fraction + p0 * (1 - fraction)
        along += alpha * interval
        block_given? ? yielder << block.call(p0, along, angle) : yielder << p0
        alpha = 1.0
      end
      distance = (p1 - p0).norm
      next alpha - distance / interval, along + distance
    end
  end.entries
end

#segmentise(interval) ⇒ Object



85
86
87
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 85

def segmentise(interval)
  LineString.new sample_at(interval).push(@coordinates.last), @properties
end

#signed_areaObject



34
35
36
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 34

def signed_area
  each_cons(2).sum { |p0, p1| p0.cross(p1) } / 2
end

#simplify(tolerance) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 48

def simplify(tolerance)
  chunks, simplified = [@coordinates], []
  while chunk = chunks.pop
    direction = (chunk.last - chunk.first).normalised
    delta, index = chunk.map do |point|
      (point - chunk.first).cross(direction).abs
    end.each.with_index.max_by(&:first)
    if delta < tolerance
      simplified.prepend chunk.first
    else
      chunks << chunk[0..index] << chunk[index..-1]
    end
  end
  simplified << @coordinates.last
  LineString.new simplified, @properties
end

#smooth_window(window) ⇒ Object



89
90
91
92
93
94
95
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 89

def smooth_window(window)
  [@coordinates.take(1)*(window-1), @coordinates, @coordinates.last(1)*(window-1)].flatten(1).each_cons(window).map do |points|
    points.inject(&:+) / window
  end.then do |smoothed|
    LineString.new smoothed, @properties
  end
end

#svg_path_data(bezier: false) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 121

def svg_path_data(bezier: false)
  if bezier
    fraction = Numeric === bezier ? bezier.clamp(0, 1) : 1
    extras = closed? ? [@coordinates[-2], *@coordinates, @coordinates[2]] : [@coordinates.first, *@coordinates, @coordinates.last]
    midpoints = extras.each_cons(2).map do |p0, p1|
      (p0 + p1) / 2
    end
    distances = extras.each_cons(2).map do |p0, p1|
      (p1 - p0).norm
    end
    offsets = midpoints.zip(distances).each_cons(2).map do |(m0, d0), (m1, d1)|
      (m0 * d1 + m1 * d0) / (d0 + d1)
    end.zip(@coordinates).map do |p0, p1|
      p1 - p0
    end
    controls = midpoints.each_cons(2).zip(offsets).flat_map do |(m0, m1), offset|
      next m0 + offset * fraction, m1 + offset * fraction
    end.drop(1).each_slice(2).entries.prepend(nil)
    zip(controls).map do |point, controls|
      controls ? "C %s %s %s" % [POINT, POINT, POINT] % [*controls.flatten, *point] : "M %s" % POINT % point
    end.join(" ")
  else
    map do |point|
      POINT % point
    end.join(" L ").tap do |string|
      string.concat(" Z") if closed?
    end.prepend("M ")
  end
end

#trim(amount) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/nswtopo/gis/geojson/line_string.rb', line 97

def trim(amount)
  return self unless amount > 0
  ending, total = path_length - amount, 0
  trimmed = each_cons(2).with_object [] do |(p0, p1), trimmed|
    delta = (p1 - p0).norm
    case
    when total >= ending then break trimmed
    when total <= amount - delta
    when total <= amount
      trimmed << (p0 * (delta + total - amount) + p1 * (amount - total)) / delta
      trimmed << (p0 * (delta + total - ending) + p1 * (ending - total)) / delta if total + delta >= ending
    else
      trimmed << p0
      trimmed << (p0 * (delta + total - ending) + p1 * (ending - total)) / delta if total + delta >= ending
    end
    total += delta
  end
  LineString.new trimmed, @properties
end