libgd-gis 0.2.8 → 0.3.0

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.
@@ -1,8 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GD
2
4
  module GIS
5
+ # Renders collections of line geometries onto a GD image.
6
+ #
7
+ # A LinesLayer draws simple line strings using a fixed stroke
8
+ # color and width. Coordinates are expected to be in
9
+ # [longitude, latitude] order and are projected at render time.
10
+ #
3
11
  class LinesLayer
12
+ # @return [Boolean] enables debug rendering
4
13
  attr_accessor :debug
5
14
 
15
+ # Creates a new lines layer.
16
+ #
17
+ # @param lines [Array<Array<Array<Float>>>]
18
+ # array of line strings ([[lng, lat], ...])
19
+ # @param stroke [GD::Color] line color
20
+ # @param width [Integer] stroke width in pixels
6
21
  def initialize(lines, stroke:, width:)
7
22
  @lines = lines
8
23
  @stroke = stroke
@@ -10,6 +25,14 @@ module GD
10
25
  @debug = false
11
26
  end
12
27
 
28
+ # Renders the lines onto the given image.
29
+ #
30
+ # @param img [GD::Image] target image
31
+ # @param projection [#call]
32
+ # callable converting (lng, lat) → (x, y)
33
+ #
34
+ # @raise [RuntimeError] if a line is invalid
35
+ # @return [void]
13
36
  def render!(img, projection)
14
37
  @lines.each do |line|
15
38
  raise "Invalid line: #{line.inspect}" unless valid_line?(line)
@@ -33,6 +56,10 @@ module GD
33
56
 
34
57
  private
35
58
 
59
+ # Validates a line string.
60
+ #
61
+ # @param line [Object]
62
+ # @return [Boolean]
36
63
  def valid_line?(line)
37
64
  line.is_a?(Array) &&
38
65
  line.size >= 2 &&
@@ -1,16 +1,54 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GD
2
4
  module GIS
5
+ # Renders point data as icons (markers) with optional labels.
6
+ #
7
+ # A PointsLayer draws markers for arbitrary data records.
8
+ # Longitude and latitude values are extracted using callables,
9
+ # allowing the layer to work with any data structure.
10
+ #
11
+ # Optionally, text labels can be rendered next to each marker.
12
+ #
3
13
  class PointsLayer
4
-
5
- def initialize(data, lon:, lat:, icon:, label: nil, font: nil, size: 12, color: [0,0,0])
14
+ # Creates a new points layer.
15
+ #
16
+ # @param data [Enumerable]
17
+ # collection of data records
18
+ # @param lon [#call]
19
+ # callable extracting longitude from a data record
20
+ # @param lat [#call]
21
+ # callable extracting latitude from a data record
22
+ # @param icon [String, Array<GD::Color>, nil]
23
+ # path to an icon image, or [fill, stroke] colors,
24
+ # or nil to generate a random marker
25
+ # @param label [#call, nil]
26
+ # callable extracting label text from a data record
27
+ # @param font [String, nil]
28
+ # font path used for labels
29
+ # @param size [Integer]
30
+ # font size in pixels
31
+ # @param color [Array<Integer>]
32
+ # label color as RGB array
33
+ def initialize(
34
+ data,
35
+ lon:,
36
+ lat:,
37
+ icon:,
38
+ label: nil,
39
+ font: nil,
40
+ size: 12,
41
+ color: [0, 0, 0]
42
+ )
6
43
  @data = data
7
44
  @lon = lon
8
45
  @lat = lat
9
46
 
10
- if icon
11
- @icon = GD::Image.open(icon)
47
+ if icon.is_a?(Array) || icon.nil?
48
+ fill, stroke = icon || [GD::GIS::ColorHelpers.random_rgb, GD::GIS::ColorHelpers.random_rgb]
49
+ @icon = build_default_marker(fill, stroke)
12
50
  else
13
- @icon = build_default_marker
51
+ @icon = GD::Image.open(icon)
14
52
  end
15
53
 
16
54
  @label = label
@@ -22,27 +60,37 @@ module GD
22
60
  @icon.save_alpha = true
23
61
  end
24
62
 
25
- def build_default_marker
63
+ # Builds a default circular marker icon.
64
+ #
65
+ # @param fill [GD::Color]
66
+ # @param stroke [GD::Color]
67
+ # @return [GD::Image]
68
+ def build_default_marker(fill, stroke)
26
69
  size = 32
70
+
27
71
  img = GD::Image.new(size, size)
28
72
  img.antialias = true
29
-
30
- white = GD::Color.rgb(255,255,255)
31
- black = GD::Color.rgb(0,0,0)
32
73
 
33
74
  cx = size / 2
34
75
  cy = size / 2
35
- r = 12
76
+ r = 5
36
77
 
37
- # borde blanco
38
- img.arc(cx, cy, r*2+4, r*2+4, 0, 360, white)
78
+ # stroke
79
+ img.arc(cx, cy, (r * 2) + 4, (r * 2) + 4, 0, 360, stroke)
39
80
 
40
- # relleno negro
41
- img.filled_arc(cx, cy, r*2, r*2, 0, 360, black)
81
+ # fill
82
+ img.filled_arc(cx, cy, r * 2, r * 2, 0, 360, fill)
42
83
 
43
84
  img
44
85
  end
45
86
 
87
+ # Renders all points onto the image.
88
+ #
89
+ # @param img [GD::Image] target image
90
+ # @param projector [#call]
91
+ # callable converting (lon, lat) → (x, y)
92
+ #
93
+ # @return [void]
46
94
  def render!(img, projector)
47
95
  w = @icon.width
48
96
  h = @icon.height
@@ -51,26 +99,25 @@ module GD
51
99
  lon = @lon.call(row)
52
100
  lat = @lat.call(row)
53
101
 
54
- x,y = projector.call(lon,lat)
102
+ x, y = projector.call(lon, lat)
55
103
 
56
104
  # icono
57
- img.copy(@icon, x - w/2, y - h/2, 0,0,w,h)
105
+ img.copy(@icon, x - (w / 2), y - (h / 2), 0, 0, w, h)
58
106
 
59
107
  # etiqueta opcional
60
- if @label && @font
61
- text = @label.call(row)
62
- unless text.nil? || text.strip.empty?
63
- font_h = @size * 1.1
64
-
65
- img.text(text,
66
- x: x + w/2 + 4,
67
- y: y + font_h/2,
68
- size: @size,
69
- color: @color,
70
- font: @font
71
- )
72
- end
73
- end
108
+ next unless @label && @font
109
+
110
+ text = @label.call(row)
111
+ next if text.nil? || text.strip.empty?
112
+
113
+ font_h = @size * 1.1
114
+
115
+ img.text(text,
116
+ x: x + (w / 2) + 4,
117
+ y: y + (font_h / 2),
118
+ size: @size,
119
+ color: @color,
120
+ font: @font)
74
121
  end
75
122
  end
76
123
  end
@@ -1,17 +1,46 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GD
2
4
  module GIS
5
+ # Renders polygon geometries onto a GD image.
6
+ #
7
+ # A PolygonsLayer draws filled polygons with optional
8
+ # stroke outlines. Polygons are expected to be provided
9
+ # as arrays of rings in [longitude, latitude] order.
10
+ #
3
11
  class PolygonsLayer
12
+ # @return [Boolean] enables debug rendering
4
13
  attr_accessor :debug
5
14
 
15
+ # Creates a new polygons layer.
16
+ #
17
+ # @param polygons [Array<Array<Array<Array<Float>>>>]
18
+ # array of polygons, each consisting of one or more rings
19
+ # @param fill [GD::Color] fill color
20
+ # @param stroke [GD::Color, nil] stroke color
21
+ # @param width [Integer, nil] stroke width in pixels
6
22
  def initialize(polygons, fill:, stroke: nil, width: nil)
7
23
  @polygons = polygons
8
24
  @fill = fill
9
25
  @stroke = stroke
10
26
  @width = width
11
-
12
- @debug = false
27
+ @debug = false
13
28
  end
14
29
 
30
+ # Builds a polygon layer by buffering line features.
31
+ #
32
+ # This is a convenience constructor that converts
33
+ # line geometries into polygon buffers using a naive
34
+ # geometric approximation.
35
+ #
36
+ # @param features [Array<Hash>]
37
+ # GeoJSON-like features with LineString geometries
38
+ # @param stroke [GD::Color]
39
+ # @param fill [GD::Color]
40
+ # @param width [Numeric]
41
+ # buffer width (approximate)
42
+ #
43
+ # @return [PolygonsLayer]
15
44
  def self.from_lines(features, stroke:, fill:, width:)
16
45
  polys = []
17
46
 
@@ -24,6 +53,13 @@ module GD
24
53
  new(polys, fill: fill, stroke: stroke)
25
54
  end
26
55
 
56
+ # Renders all polygons onto the image.
57
+ #
58
+ # @param img [GD::Image] target image
59
+ # @param projection [#call]
60
+ # callable converting (lng, lat) → (x, y)
61
+ #
62
+ # @return [void]
27
63
  def render!(img, projection)
28
64
  @polygons.each do |polygon|
29
65
  # polygon = [ ring, ring, ... ]
@@ -35,20 +71,19 @@ module GD
35
71
  @stroke = GD::GIS::ColorHelpers.random_vivid if @debug
36
72
  @fill = GD::GIS::ColorHelpers.random_vivid if @debug
37
73
 
38
- if idx == 0
74
+ if idx.zero?
39
75
  # ring exterior
40
76
  img.filled_polygon(pts, @fill)
41
77
  end
42
78
 
43
- if @stroke
44
- pts.each_cons(2) do |a, b|
45
- img.line(a[0], a[1], b[0], b[1], @stroke, thickness: (@width || 1))
46
- end
79
+ next unless @stroke
80
+
81
+ pts.each_cons(2) do |a, b|
82
+ img.line(a[0], a[1], b[0], b[1], @stroke, thickness: @width || 1)
47
83
  end
48
84
  end
49
85
  end
50
86
  end
51
-
52
87
  end
53
88
  end
54
89
  end