libgd-gis 0.3.21 → 0.4.1
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.
- checksums.yaml +4 -4
- data/lib/gd/gis/crs_normalizer.rb +67 -0
- data/lib/gd/gis/layer_points.rb +14 -11
- data/lib/gd/gis/legend.rb +20 -0
- data/lib/gd/gis/map.rb +203 -1
- data/lib/gd/gis/style.rb +5 -0
- metadata +3 -4
- data/lib/gd/gis/middleware.rb +0 -152
- data/lib/test.rb +0 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ff2a73817209bdd6e71f77b83cd0f7ffaee27327e95c199e01bf9eb879b2ff53
|
|
4
|
+
data.tar.gz: d5fe87d75561eba31a1cae4c46902a16dfcd9a70b525f8d6772682f05f1f9c56
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a9e41b51e0e1365eb60905131e2766981de954d74b92b16ba8abb45224e3c6df9215a96628cf23f7c90c6675d2acf98dbafe326c569ee45440dcedd1502f5d7d
|
|
7
|
+
data.tar.gz: d2598e4392ee4c400a0cf2982d91916119222f9ec4dd869b4851d0b994a0a241f39ef483e720d66121fd070af3b4ba5c46068d2194d8a09b9ed4b61cfa259192
|
|
@@ -69,6 +69,9 @@ module GD
|
|
|
69
69
|
when EPSG3857
|
|
70
70
|
mercator_to_wgs84(lon, lat)
|
|
71
71
|
|
|
72
|
+
when "EPSG:22195"
|
|
73
|
+
gk_to_wgs84(lon, lat)
|
|
74
|
+
|
|
72
75
|
else
|
|
73
76
|
raise ArgumentError, "Unsupported CRS: #{@crs}"
|
|
74
77
|
end
|
|
@@ -97,6 +100,70 @@ module GD
|
|
|
97
100
|
lat = ((2 * Math.atan(Math.exp(y / r))) - (Math::PI / 2)) * 180.0 / Math::PI
|
|
98
101
|
[lon, lat]
|
|
99
102
|
end
|
|
103
|
+
|
|
104
|
+
# Converts Gauss–Krüger (GK) projected coordinates to WGS84.
|
|
105
|
+
#
|
|
106
|
+
# This method converts easting/northing coordinates from
|
|
107
|
+
# Gauss–Krüger Argentina Zone 5 (EPSG:22195) into
|
|
108
|
+
# WGS84 longitude/latitude (degrees).
|
|
109
|
+
#
|
|
110
|
+
# The implementation is intended for cartographic rendering
|
|
111
|
+
# and visualization purposes, not for high-precision geodesy.
|
|
112
|
+
#
|
|
113
|
+
# @param easting [Numeric]
|
|
114
|
+
# Easting value in meters.
|
|
115
|
+
#
|
|
116
|
+
# @param northing [Numeric]
|
|
117
|
+
# Northing value in meters.
|
|
118
|
+
#
|
|
119
|
+
# @return [Array<Float>]
|
|
120
|
+
# A `[longitude, latitude]` pair in decimal degrees (WGS84).
|
|
121
|
+
#
|
|
122
|
+
# @example Convert Gauss–Krüger coordinates
|
|
123
|
+
# gk_to_wgs84(580_000, 6_176_000)
|
|
124
|
+
# # => [longitude, latitude]
|
|
125
|
+
#
|
|
126
|
+
# @note
|
|
127
|
+
# This method assumes:
|
|
128
|
+
# - Central meridian: −60°
|
|
129
|
+
# - False easting: 500,000 m
|
|
130
|
+
# - WGS84-compatible ellipsoid
|
|
131
|
+
#
|
|
132
|
+
# @see https://epsg.io/22195
|
|
133
|
+
|
|
134
|
+
def gk_to_wgs84(easting, northing)
|
|
135
|
+
a = 6378137.0
|
|
136
|
+
f = 1 / 298.257223563
|
|
137
|
+
e2 = (2 * f) - (f * f)
|
|
138
|
+
lon0 = -60.0 * Math::PI / 180.0
|
|
139
|
+
|
|
140
|
+
x = easting - 500_000.0
|
|
141
|
+
y = northing - 10_000_000.0
|
|
142
|
+
|
|
143
|
+
m = y
|
|
144
|
+
mu = m / (a * (1 - (e2 / 4) - (3 * e2 * e2 / 64)))
|
|
145
|
+
|
|
146
|
+
e1 = (1 - Math.sqrt(1 - e2)) / (1 + Math.sqrt(1 - e2))
|
|
147
|
+
|
|
148
|
+
j1 = (3 * e1 / 2) - (27 * (e1**3) / 32)
|
|
149
|
+
j2 = (21 * (e1**2) / 16) - (55 * (e1**4) / 32)
|
|
150
|
+
|
|
151
|
+
fp = mu + (j1 * Math.sin(2 * mu)) + (j2 * Math.sin(4 * mu))
|
|
152
|
+
|
|
153
|
+
c1 = e2 * (Math.cos(fp)**2)
|
|
154
|
+
t1 = Math.tan(fp)**2
|
|
155
|
+
r1 = a * (1 - e2) / ((1 - (e2 * (Math.sin(fp)**2)))**1.5)
|
|
156
|
+
n1 = a / Math.sqrt(1 - (e2 * (Math.sin(fp)**2)))
|
|
157
|
+
|
|
158
|
+
d = x / n1
|
|
159
|
+
|
|
160
|
+
lat = fp - ((n1 * Math.tan(fp) / r1) *
|
|
161
|
+
(((d**2) / 2) - ((5 + (3 * t1) + (10 * c1)) * (d**4) / 24)))
|
|
162
|
+
|
|
163
|
+
lon = lon0 + ((d - ((1 + (2 * t1) + c1) * (d**3) / 6)) / Math.cos(fp))
|
|
164
|
+
|
|
165
|
+
[lon * 180.0 / Math::PI, lat * 180.0 / Math::PI]
|
|
166
|
+
end
|
|
100
167
|
end
|
|
101
168
|
end
|
|
102
169
|
end
|
data/lib/gd/gis/layer_points.rb
CHANGED
|
@@ -40,19 +40,19 @@ module GD
|
|
|
40
40
|
size: 12,
|
|
41
41
|
color: [0, 0, 0],
|
|
42
42
|
font_color: nil,
|
|
43
|
-
|
|
43
|
+
symbol: 0
|
|
44
44
|
)
|
|
45
45
|
@data = data
|
|
46
46
|
@lon = lon
|
|
47
47
|
@lat = lat
|
|
48
48
|
@color = color
|
|
49
|
+
@font_color = font_color
|
|
49
50
|
|
|
50
51
|
if icon.is_a?(Array) || icon.nil?
|
|
51
52
|
fill, stroke = icon || [GD::GIS::ColorHelpers.random_rgb, GD::GIS::ColorHelpers.random_rgb]
|
|
52
53
|
@icon = build_default_marker(fill, stroke)
|
|
53
|
-
elsif %w[numeric alphabetic].include?(icon)
|
|
54
|
+
elsif %w[numeric alphabetic symbol].include?(icon)
|
|
54
55
|
@icon = icon
|
|
55
|
-
@font_color = font_color
|
|
56
56
|
else
|
|
57
57
|
@icon = GD::Image.open(icon)
|
|
58
58
|
@icon.alpha_blending = true
|
|
@@ -62,9 +62,11 @@ module GD
|
|
|
62
62
|
@label = label
|
|
63
63
|
@font = font
|
|
64
64
|
@size = size
|
|
65
|
+
|
|
65
66
|
@r, @g, @b, @a = color
|
|
66
67
|
@a = 0 if @a.nil?
|
|
67
|
-
|
|
68
|
+
|
|
69
|
+
@symbol = symbol
|
|
68
70
|
end
|
|
69
71
|
|
|
70
72
|
# Builds a default circular marker icon.
|
|
@@ -101,11 +103,13 @@ module GD
|
|
|
101
103
|
def render!(img, projector)
|
|
102
104
|
value = case @icon
|
|
103
105
|
when "numeric"
|
|
104
|
-
@
|
|
106
|
+
@symbol
|
|
105
107
|
when "alphabetic"
|
|
106
|
-
(@
|
|
108
|
+
(@symbol + 96).chr
|
|
109
|
+
when "symbol"
|
|
110
|
+
@symbol
|
|
107
111
|
else
|
|
108
|
-
|
|
112
|
+
@icon
|
|
109
113
|
end
|
|
110
114
|
|
|
111
115
|
if @icon.is_a?(GD::Image)
|
|
@@ -128,8 +132,7 @@ module GD
|
|
|
128
132
|
|
|
129
133
|
font_h = @size * 1.1
|
|
130
134
|
|
|
131
|
-
if @icon == "numeric" || @icon == "alphabetic"
|
|
132
|
-
|
|
135
|
+
if @icon == "numeric" || @icon == "alphabetic" || @icon == "symbol"
|
|
133
136
|
draw_symbol_circle!(
|
|
134
137
|
img: img,
|
|
135
138
|
x: x,
|
|
@@ -148,7 +151,7 @@ module GD
|
|
|
148
151
|
x: x + (w / 2) + 4,
|
|
149
152
|
y: y + (font_h / 2),
|
|
150
153
|
size: @size,
|
|
151
|
-
color:
|
|
154
|
+
color: @font_color,
|
|
152
155
|
font: @font)
|
|
153
156
|
end
|
|
154
157
|
end
|
|
@@ -173,7 +176,7 @@ module GD
|
|
|
173
176
|
# baseline = top_y + h = y + h/2
|
|
174
177
|
text_x = (x - (w / 2.0)).round
|
|
175
178
|
text_y = (y + (h / 2.0)).round
|
|
176
|
-
|
|
179
|
+
|
|
177
180
|
# 4) Draw number
|
|
178
181
|
img.text(
|
|
179
182
|
text,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# lib/libgd/gis/legend.rb
|
|
2
|
+
|
|
3
|
+
module GD
|
|
4
|
+
module GIS
|
|
5
|
+
LegendItem = Struct.new(:color, :label)
|
|
6
|
+
|
|
7
|
+
class Legend
|
|
8
|
+
attr_reader :items, :position
|
|
9
|
+
|
|
10
|
+
def initialize(position: :bottom_right)
|
|
11
|
+
@position = position
|
|
12
|
+
@items = []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def add(color, label)
|
|
16
|
+
@items << LegendItem.new(color, label)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/gd/gis/map.rb
CHANGED
|
@@ -7,6 +7,7 @@ require_relative "layer_geojson"
|
|
|
7
7
|
require_relative "layer_points"
|
|
8
8
|
require_relative "layer_lines"
|
|
9
9
|
require_relative "layer_polygons"
|
|
10
|
+
require_relative "legend"
|
|
10
11
|
|
|
11
12
|
LINE_GEOMS = %w[LineString MultiLineString].freeze
|
|
12
13
|
POLY_GEOMS = %w[Polygon MultiPolygon].freeze
|
|
@@ -42,6 +43,8 @@ module GD
|
|
|
42
43
|
# @return [Boolean] enables debug rendering
|
|
43
44
|
attr_reader :debug
|
|
44
45
|
|
|
46
|
+
attr_reader :legend
|
|
47
|
+
|
|
45
48
|
# Creates a new map.
|
|
46
49
|
#
|
|
47
50
|
# @param bbox [Array<Float>]
|
|
@@ -217,6 +220,111 @@ module GD
|
|
|
217
220
|
@used_labels[key] = true
|
|
218
221
|
end
|
|
219
222
|
|
|
223
|
+
def legend(position: :bottom_right)
|
|
224
|
+
@legend = Legend.new(position: position)
|
|
225
|
+
yield @legend
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def legend_from_layers(position: :bottom_right)
|
|
229
|
+
@legend = Legend.new(position: position)
|
|
230
|
+
|
|
231
|
+
layers.each do |layer|
|
|
232
|
+
next unless layer.respond_to?(:color)
|
|
233
|
+
@legend.add(layer.color, layer.name)
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def draw_legend
|
|
238
|
+
return unless @legend
|
|
239
|
+
return unless @image
|
|
240
|
+
return unless @style
|
|
241
|
+
return unless @style.global
|
|
242
|
+
return if @style.global[:label] == false
|
|
243
|
+
|
|
244
|
+
label_style = @style.global[:label] || {}
|
|
245
|
+
|
|
246
|
+
padding = 10
|
|
247
|
+
box_size = 12
|
|
248
|
+
line_height = 18
|
|
249
|
+
margin = 15
|
|
250
|
+
|
|
251
|
+
# --- font (from style) -----------------------------------
|
|
252
|
+
|
|
253
|
+
font_path =
|
|
254
|
+
case label_style[:font]
|
|
255
|
+
when nil, "default"
|
|
256
|
+
GD::GIS::FontHelper.random
|
|
257
|
+
else
|
|
258
|
+
label_style[:font]
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
font_size = label_style[:size] || 10
|
|
262
|
+
font_color = GD::Color.rgba(*(label_style[:color] || [0, 0, 0, 0]))
|
|
263
|
+
|
|
264
|
+
# --- measure text (CORRECT API) ---------------------------
|
|
265
|
+
|
|
266
|
+
text_widths = @legend.items.map do |i|
|
|
267
|
+
w, = @image.text_bbox(
|
|
268
|
+
i.label,
|
|
269
|
+
font: font_path,
|
|
270
|
+
size: font_size
|
|
271
|
+
)
|
|
272
|
+
w
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
width = (text_widths.max || 0) + box_size + padding * 3
|
|
276
|
+
height = @legend.items.size * line_height + padding * 2
|
|
277
|
+
|
|
278
|
+
# --- position --------------------------------------------
|
|
279
|
+
|
|
280
|
+
x, y =
|
|
281
|
+
case @legend.position
|
|
282
|
+
when :bottom_right
|
|
283
|
+
[@image.width - width - margin, @image.height - height - margin]
|
|
284
|
+
when :bottom_left
|
|
285
|
+
[margin, @image.height - height - margin]
|
|
286
|
+
when :top_right
|
|
287
|
+
[@image.width - width - margin, margin]
|
|
288
|
+
when :top_left
|
|
289
|
+
[margin, margin]
|
|
290
|
+
else
|
|
291
|
+
[margin, margin]
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# --- background ------------------------------------------
|
|
295
|
+
|
|
296
|
+
bg = GD::Color.rgba(255, 255, 255, 80)
|
|
297
|
+
border = GD::Color.rgb(200, 200, 200)
|
|
298
|
+
|
|
299
|
+
@image.filled_rectangle(x, y, x + width, y + height, bg)
|
|
300
|
+
@image.rectangle(x, y, x + width, y + height, border)
|
|
301
|
+
|
|
302
|
+
# --- items -----------------------------------------------
|
|
303
|
+
|
|
304
|
+
@legend.items.each_with_index do |item, idx|
|
|
305
|
+
iy = y + padding + idx * line_height
|
|
306
|
+
|
|
307
|
+
# color box
|
|
308
|
+
@image.filled_rectangle(
|
|
309
|
+
x + padding,
|
|
310
|
+
iy,
|
|
311
|
+
x + padding + box_size,
|
|
312
|
+
iy + box_size,
|
|
313
|
+
GD::Color.rgba(*item.color)
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# label text
|
|
317
|
+
@image.text_ft(
|
|
318
|
+
item.label,
|
|
319
|
+
x: x + padding + box_size + 8,
|
|
320
|
+
y: iy + box_size,
|
|
321
|
+
font: font_path,
|
|
322
|
+
size: font_size,
|
|
323
|
+
color: font_color
|
|
324
|
+
)
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
220
328
|
# Loads features from a GeoJSON file.
|
|
221
329
|
#
|
|
222
330
|
# This method:
|
|
@@ -288,7 +396,7 @@ module GD
|
|
|
288
396
|
size: size,
|
|
289
397
|
color: color,
|
|
290
398
|
font_color: font_color,
|
|
291
|
-
|
|
399
|
+
symbol: @count
|
|
292
400
|
)
|
|
293
401
|
@count += 1
|
|
294
402
|
elsif LINE_GEOMS.include?(geom_type)
|
|
@@ -298,6 +406,96 @@ module GD
|
|
|
298
406
|
end
|
|
299
407
|
end
|
|
300
408
|
|
|
409
|
+
# Adds a single point (marker) to the map.
|
|
410
|
+
#
|
|
411
|
+
# This is a convenience helper for the most common use case: rendering
|
|
412
|
+
# one point with an optional label and icon, without having to build
|
|
413
|
+
# a full collection or manually configure a PointsLayer.
|
|
414
|
+
#
|
|
415
|
+
# Internally, this method wraps the given coordinates into a one-element
|
|
416
|
+
# data collection and delegates rendering to {GD::GIS::PointsLayer},
|
|
417
|
+
# preserving the same rendering behavior and options.
|
|
418
|
+
#
|
|
419
|
+
# This method is intended for annotations, markers, alerts, cities,
|
|
420
|
+
# or any scenario where only one point needs to be rendered.
|
|
421
|
+
#
|
|
422
|
+
# @param lon [Numeric]
|
|
423
|
+
# Longitude of the point.
|
|
424
|
+
# @param lat [Numeric]
|
|
425
|
+
# Latitude of the point.
|
|
426
|
+
# @param label [String, nil]
|
|
427
|
+
# Optional text label rendered next to the point.
|
|
428
|
+
# @param icon [String, Array<GD::Color>, nil]
|
|
429
|
+
# Marker representation. Can be:
|
|
430
|
+
# - a path to an image file
|
|
431
|
+
# - :numeric or :alphabetic symbol styles
|
|
432
|
+
# - an array of [fill, stroke] colors
|
|
433
|
+
# - nil to generate a default marker
|
|
434
|
+
# @param font [String, nil]
|
|
435
|
+
# Font path used to render the label or symbol.
|
|
436
|
+
# @param size [Integer]
|
|
437
|
+
# Font size in pixels (default: 12).
|
|
438
|
+
# @param color [Array<Integer>]
|
|
439
|
+
# Label or symbol background color as an RGB(A) array.
|
|
440
|
+
# @param font_color [GD::Color, nil]
|
|
441
|
+
# Text color for numeric or alphabetic symbols.
|
|
442
|
+
#
|
|
443
|
+
# @return [void]
|
|
444
|
+
#
|
|
445
|
+
# @example Render a simple point
|
|
446
|
+
# map.add_point(
|
|
447
|
+
# lon: -58.3816,
|
|
448
|
+
# lat: -34.6037
|
|
449
|
+
# )
|
|
450
|
+
#
|
|
451
|
+
# @example Point with label
|
|
452
|
+
# map.add_point(
|
|
453
|
+
# lon: -58.3816,
|
|
454
|
+
# lat: -34.6037,
|
|
455
|
+
# label: "Buenos Aires"
|
|
456
|
+
# )
|
|
457
|
+
#
|
|
458
|
+
# @example Point with numeric marker
|
|
459
|
+
# map.add_point(
|
|
460
|
+
# lon: -58.3816,
|
|
461
|
+
# lat: -34.6037,
|
|
462
|
+
# icon: "numeric",
|
|
463
|
+
# label: "1",
|
|
464
|
+
# font: "/usr/share/fonts/DejaVuSans.ttf"
|
|
465
|
+
# )
|
|
466
|
+
#
|
|
467
|
+
|
|
468
|
+
def add_point(
|
|
469
|
+
lon:,
|
|
470
|
+
lat:,
|
|
471
|
+
label: nil,
|
|
472
|
+
icon: nil,
|
|
473
|
+
font: nil,
|
|
474
|
+
size: nil,
|
|
475
|
+
color: nil,
|
|
476
|
+
font_color: nil,
|
|
477
|
+
symbol: nil
|
|
478
|
+
)
|
|
479
|
+
row = {
|
|
480
|
+
lon: lon,
|
|
481
|
+
lat: lat,
|
|
482
|
+
label: label
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
@points_layers << GD::GIS::PointsLayer.new(
|
|
486
|
+
[row],
|
|
487
|
+
lon: -> r { r[:lon] },
|
|
488
|
+
lat: -> r { r[:lat] },
|
|
489
|
+
icon: icon || @style.point[:icon],
|
|
490
|
+
label: label ? -> r { r[:label] } : nil,
|
|
491
|
+
font: font || @style.point[:font],
|
|
492
|
+
size: size || @style.point[:size],
|
|
493
|
+
color: color || @style.point[:color],
|
|
494
|
+
font_color: font_color || @style.point[:font_color],
|
|
495
|
+
symbol: symbol
|
|
496
|
+
)
|
|
497
|
+
end
|
|
498
|
+
|
|
301
499
|
# Adds a generic points overlay layer.
|
|
302
500
|
#
|
|
303
501
|
# @param data [Enumerable]
|
|
@@ -387,6 +585,8 @@ module GD
|
|
|
387
585
|
@polygons_layers.each { |l| l.render!(@image, projection) }
|
|
388
586
|
@lines_layers.each { |l| l.render!(@image, projection) }
|
|
389
587
|
@points_layers.each { |l| l.render!(@image, projection) }
|
|
588
|
+
|
|
589
|
+
draw_legend
|
|
390
590
|
end
|
|
391
591
|
|
|
392
592
|
def render_viewport
|
|
@@ -446,6 +646,8 @@ module GD
|
|
|
446
646
|
@polygons_layers.each { |l| l.render!(@image, projection) }
|
|
447
647
|
@lines_layers.each { |l| l.render!(@image, projection) }
|
|
448
648
|
@points_layers.each { |l| l.render!(@image, projection) }
|
|
649
|
+
|
|
650
|
+
draw_legend
|
|
449
651
|
end
|
|
450
652
|
|
|
451
653
|
# Saves the rendered image to disk.
|
data/lib/gd/gis/style.rb
CHANGED
|
@@ -17,6 +17,9 @@ module GD
|
|
|
17
17
|
# @return [Hash] global styling rules
|
|
18
18
|
attr_reader :global
|
|
19
19
|
|
|
20
|
+
# @return [Hash] point styling rules
|
|
21
|
+
attr_reader :point
|
|
22
|
+
|
|
20
23
|
# @return [Hash] road styling rules
|
|
21
24
|
attr_reader :roads
|
|
22
25
|
|
|
@@ -45,6 +48,7 @@ module GD
|
|
|
45
48
|
# :global, :roads, :rails, :water, :parks, :points, :order, :track
|
|
46
49
|
def initialize(definition)
|
|
47
50
|
@global = definition[:global] || {}
|
|
51
|
+
@point = definition[:point] || {}
|
|
48
52
|
@roads = definition[:roads] || {}
|
|
49
53
|
@rails = definition[:rails] || {}
|
|
50
54
|
@water = definition[:water] || {}
|
|
@@ -79,6 +83,7 @@ module GD
|
|
|
79
83
|
|
|
80
84
|
new(
|
|
81
85
|
global: data[:global],
|
|
86
|
+
point: data[:point],
|
|
82
87
|
roads: data[:roads],
|
|
83
88
|
rails: data[:rail] || data[:rails],
|
|
84
89
|
track: data[:track],
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: libgd-gis
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Germán Alberto Giménez Silva
|
|
@@ -50,14 +50,13 @@ files:
|
|
|
50
50
|
- lib/gd/gis/layer_lines.rb
|
|
51
51
|
- lib/gd/gis/layer_points.rb
|
|
52
52
|
- lib/gd/gis/layer_polygons.rb
|
|
53
|
+
- lib/gd/gis/legend.rb
|
|
53
54
|
- lib/gd/gis/map.rb
|
|
54
|
-
- lib/gd/gis/middleware.rb
|
|
55
55
|
- lib/gd/gis/ontology.rb
|
|
56
56
|
- lib/gd/gis/ontology.yml
|
|
57
57
|
- lib/gd/gis/projection.rb
|
|
58
58
|
- lib/gd/gis/style.rb
|
|
59
59
|
- lib/libgd_gis.rb
|
|
60
|
-
- lib/test.rb
|
|
61
60
|
homepage: https://github.com/ggerman/libgd-gis
|
|
62
61
|
licenses:
|
|
63
62
|
- MIT
|
|
@@ -77,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
77
76
|
- !ruby/object:Gem::Version
|
|
78
77
|
version: '0'
|
|
79
78
|
requirements: []
|
|
80
|
-
rubygems_version: 4.0.
|
|
79
|
+
rubygems_version: 4.0.6
|
|
81
80
|
specification_version: 4
|
|
82
81
|
summary: Geospatial raster rendering for Ruby using libgd
|
|
83
82
|
test_files: []
|
data/lib/gd/gis/middleware.rb
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module GD
|
|
4
|
-
module GIS
|
|
5
|
-
# Coordinate Reference System (CRS) helpers and normalizers.
|
|
6
|
-
#
|
|
7
|
-
# This module defines commonly used CRS identifiers and
|
|
8
|
-
# utilities for normalizing coordinates into a single,
|
|
9
|
-
# consistent representation.
|
|
10
|
-
#
|
|
11
|
-
module CRS
|
|
12
|
-
# OGC CRS84 (longitude, latitude)
|
|
13
|
-
CRS84 = "urn:ogc:def:crs:OGC:1.3:CRS84"
|
|
14
|
-
|
|
15
|
-
# EPSG:4326 (latitude, longitude axis order)
|
|
16
|
-
EPSG4326 = "EPSG:4326"
|
|
17
|
-
|
|
18
|
-
# EPSG:3857 (Web Mercator)
|
|
19
|
-
EPSG3857 = "EPSG:3857"
|
|
20
|
-
|
|
21
|
-
# Gauss–Krüger Argentina, zone 5
|
|
22
|
-
#
|
|
23
|
-
# Note: This constant represents a *specific* GK zone
|
|
24
|
-
# and is not a generic Gauss–Krüger definition.
|
|
25
|
-
GK_ARGENTINA = "EPSG:22195"
|
|
26
|
-
|
|
27
|
-
# Normalizes coordinates from supported CRS definitions
|
|
28
|
-
# into CRS84 (longitude, latitude in degrees).
|
|
29
|
-
#
|
|
30
|
-
# Supported input CRS:
|
|
31
|
-
# - CRS84
|
|
32
|
-
# - EPSG:4326 (axis order normalization)
|
|
33
|
-
# - EPSG:3857 (Web Mercator)
|
|
34
|
-
# - EPSG:22195 (Gauss–Krüger Argentina, zone 5)
|
|
35
|
-
#
|
|
36
|
-
# All outputs are returned as:
|
|
37
|
-
# [longitude, latitude] in degrees
|
|
38
|
-
#
|
|
39
|
-
# ⚠️ Projection conversions are intended for mapping
|
|
40
|
-
# and visualization, not for high-precision geodesy.
|
|
41
|
-
#
|
|
42
|
-
class Normalizer
|
|
43
|
-
# Creates a new CRS normalizer.
|
|
44
|
-
#
|
|
45
|
-
# @param crs [String, Symbol, nil]
|
|
46
|
-
# CRS identifier; defaults to CRS84 if nil
|
|
47
|
-
def initialize(crs)
|
|
48
|
-
@crs = normalize_name(crs)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Normalizes a coordinate pair into CRS84.
|
|
52
|
-
#
|
|
53
|
-
# @param lon [Numeric]
|
|
54
|
-
# first coordinate (meaning depends on input CRS)
|
|
55
|
-
# @param lat [Numeric]
|
|
56
|
-
# second coordinate (meaning depends on input CRS)
|
|
57
|
-
#
|
|
58
|
-
# @return [Array<Float>]
|
|
59
|
-
# normalized [longitude, latitude] in degrees
|
|
60
|
-
#
|
|
61
|
-
# @raise [RuntimeError]
|
|
62
|
-
# if the CRS is not supported
|
|
63
|
-
def normalize(lon, lat)
|
|
64
|
-
case @crs
|
|
65
|
-
when CRS84
|
|
66
|
-
[lon, lat]
|
|
67
|
-
|
|
68
|
-
when EPSG4326
|
|
69
|
-
# EPSG:4326 uses (lat, lon)
|
|
70
|
-
[lat, lon]
|
|
71
|
-
|
|
72
|
-
when GK_ARGENTINA
|
|
73
|
-
gk_to_wgs84(lon, lat)
|
|
74
|
-
|
|
75
|
-
when EPSG3857
|
|
76
|
-
mercator_to_wgs84(lon, lat)
|
|
77
|
-
|
|
78
|
-
else
|
|
79
|
-
raise "Unsupported CRS: #{@crs}"
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
private
|
|
84
|
-
|
|
85
|
-
# Normalizes a CRS name into a comparable string.
|
|
86
|
-
#
|
|
87
|
-
# @param name [Object]
|
|
88
|
-
# @return [String]
|
|
89
|
-
def normalize_name(name)
|
|
90
|
-
return CRS84 if name.nil?
|
|
91
|
-
|
|
92
|
-
name.to_s.strip
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
# Converts Web Mercator coordinates to WGS84.
|
|
96
|
-
#
|
|
97
|
-
# @param x [Numeric] X coordinate in meters
|
|
98
|
-
# @param y [Numeric] Y coordinate in meters
|
|
99
|
-
# @return [Array<Float>] [longitude, latitude] in degrees
|
|
100
|
-
def mercator_to_wgs84(x, y)
|
|
101
|
-
r = 6378137.0
|
|
102
|
-
lon = (x / r) * 180.0 / Math::PI
|
|
103
|
-
lat = ((2 * Math.atan(Math.exp(y / r))) - (Math::PI / 2)) * 180.0 / Math::PI
|
|
104
|
-
[lon, lat]
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# Converts Gauss–Krüger Argentina (zone 5) coordinates to WGS84.
|
|
108
|
-
#
|
|
109
|
-
# This implementation provides sufficient accuracy for
|
|
110
|
-
# cartographic rendering and visualization.
|
|
111
|
-
#
|
|
112
|
-
# @param easting [Numeric] easting (meters)
|
|
113
|
-
# @param northing [Numeric] northing (meters)
|
|
114
|
-
# @return [Array<Float>] [longitude, latitude] in degrees
|
|
115
|
-
def gk_to_wgs84(easting, northing)
|
|
116
|
-
# Parameters for Argentina GK Zone 5
|
|
117
|
-
a = 6378137.0
|
|
118
|
-
f = 1 / 298.257223563
|
|
119
|
-
e2 = (2 * f) - (f * f)
|
|
120
|
-
lon0 = -60.0 * Math::PI / 180.0 # central meridian zone 5
|
|
121
|
-
|
|
122
|
-
x = easting - 500000.0
|
|
123
|
-
y = northing
|
|
124
|
-
|
|
125
|
-
m = y
|
|
126
|
-
mu = m / (a * (1 - (e2 / 4) - (3 * e2 * e2 / 64)))
|
|
127
|
-
|
|
128
|
-
e1 = (1 - Math.sqrt(1 - e2)) / (1 + Math.sqrt(1 - e2))
|
|
129
|
-
|
|
130
|
-
j1 = (3 * e1 / 2) - (27 * (e1**3) / 32)
|
|
131
|
-
j2 = (21 * (e1**2) / 16) - (55 * (e1**4) / 32)
|
|
132
|
-
|
|
133
|
-
fp = mu + (j1 * Math.sin(2 * mu)) + (j2 * Math.sin(4 * mu))
|
|
134
|
-
|
|
135
|
-
c1 = e2 * (Math.cos(fp)**2)
|
|
136
|
-
t1 = Math.tan(fp)**2
|
|
137
|
-
r1 = a * (1 - e2) / ((1 - (e2 * (Math.sin(fp)**2)))**1.5)
|
|
138
|
-
n1 = a / Math.sqrt(1 - (e2 * (Math.sin(fp)**2)))
|
|
139
|
-
|
|
140
|
-
d = x / n1
|
|
141
|
-
|
|
142
|
-
lat = fp - ((n1 * Math.tan(fp) / r1) *
|
|
143
|
-
(((d**2) / 2) - ((5 + (3 * t1) + (10 * c1)) * (d**4) / 24)))
|
|
144
|
-
|
|
145
|
-
lon = lon0 + ((d - ((1 + (2 * t1) + c1) * (d**3) / 6)) / Math.cos(fp))
|
|
146
|
-
|
|
147
|
-
[lon * 180.0 / Math::PI, lat * 180.0 / Math::PI]
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
end
|
data/lib/test.rb
DELETED