proj4rb 4.1.1 → 5.0.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.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +98 -0
  3. data/Gemfile +4 -4
  4. data/README.md +53 -0
  5. data/lib/api/proj.rb +750 -0
  6. data/lib/api/proj_experimental.rb +7 -0
  7. data/lib/api/proj_ffi.rb +47 -0
  8. data/lib/api/proj_version.rb +26 -0
  9. data/lib/examples/axis_order_normalization.rb +13 -0
  10. data/lib/examples/batch_transformation.rb +25 -0
  11. data/lib/examples/context_logging.rb +26 -0
  12. data/lib/examples/crs_identification.rb +18 -0
  13. data/lib/examples/database_query.rb +27 -0
  14. data/lib/examples/geodetic_distance.rb +38 -0
  15. data/lib/examples/geodetic_to_projected.rb +18 -0
  16. data/lib/examples/operation_factory_context.rb +19 -0
  17. data/lib/examples/pipeline_operator.rb +21 -0
  18. data/lib/examples/promote_demote_3d.rb +23 -0
  19. data/lib/examples/serialization_formats.rb +17 -0
  20. data/lib/examples/transform_bounds.rb +18 -0
  21. data/lib/examples/transformation_with_area.rb +18 -0
  22. data/lib/proj/area.rb +74 -74
  23. data/lib/proj/axis_info.rb +44 -44
  24. data/lib/proj/bounds.rb +22 -0
  25. data/lib/proj/bounds3d.rb +45 -0
  26. data/lib/proj/context.rb +57 -23
  27. data/lib/proj/conversion.rb +94 -91
  28. data/lib/proj/coordinate.rb +304 -281
  29. data/lib/proj/coordinate_metadata.rb +38 -38
  30. data/lib/proj/coordinate_operation_mixin.rb +464 -381
  31. data/lib/proj/coordinate_system.rb +143 -137
  32. data/lib/proj/crs.rb +688 -680
  33. data/lib/proj/crs_info.rb +47 -47
  34. data/lib/proj/database.rb +310 -305
  35. data/lib/proj/datum.rb +32 -32
  36. data/lib/proj/datum_ensemble.rb +34 -34
  37. data/lib/proj/domain.rb +82 -0
  38. data/lib/proj/ellipsoid.rb +77 -77
  39. data/lib/proj/error.rb +7 -8
  40. data/lib/proj/file_api_callbacks.rb +165 -0
  41. data/lib/proj/grid.rb +121 -121
  42. data/lib/proj/grid_cache.rb +65 -64
  43. data/lib/proj/grid_info.rb +19 -19
  44. data/lib/proj/life_span.rb +21 -0
  45. data/lib/proj/network_api_callbacks.rb +86 -0
  46. data/lib/proj/operation.rb +66 -42
  47. data/lib/proj/operation_factory_context.rb +4 -2
  48. data/lib/proj/options.rb +41 -0
  49. data/lib/proj/parameter.rb +37 -37
  50. data/lib/proj/parameters.rb +106 -107
  51. data/lib/proj/pj_axis_description.rb +26 -0
  52. data/lib/proj/pj_object.rb +602 -672
  53. data/lib/proj/pj_objects.rb +45 -45
  54. data/lib/proj/pj_param_description.rb +28 -0
  55. data/lib/proj/prime_meridian.rb +65 -65
  56. data/lib/proj/projection.rb +54 -25
  57. data/lib/proj/session.rb +2 -0
  58. data/lib/proj/transformation.rb +102 -102
  59. data/lib/proj/unit.rb +81 -108
  60. data/lib/proj.rb +10 -3
  61. data/lib/proj4.rb +5 -5
  62. data/proj4rb.gemspec +10 -5
  63. data/test/abstract_test.rb +7 -5
  64. data/test/context_test.rb +210 -172
  65. data/test/context_validation_test.rb +11 -0
  66. data/test/conversion_test.rb +376 -368
  67. data/test/coordinate_metadata_test.rb +34 -0
  68. data/test/coordinate_system_test.rb +162 -144
  69. data/test/coordinate_test.rb +289 -34
  70. data/test/crs_test.rb +1112 -1082
  71. data/test/database_test.rb +407 -391
  72. data/test/datum_ensemble_test.rb +64 -64
  73. data/test/datum_test.rb +61 -54
  74. data/test/domain_test.rb +72 -0
  75. data/test/ellipsoid_test.rb +80 -80
  76. data/test/examples_test.rb +149 -0
  77. data/test/file_api_example.rb +58 -0
  78. data/test/file_api_test.rb +74 -66
  79. data/test/grid_cache_test.rb +72 -72
  80. data/test/grid_test.rb +126 -141
  81. data/test/network_api_example.rb +48 -0
  82. data/test/network_api_test.rb +33 -45
  83. data/test/operation_factory_context_test.rb +225 -205
  84. data/test/operation_test.rb +40 -29
  85. data/test/options_test.rb +17 -0
  86. data/test/parameters_test.rb +86 -40
  87. data/test/pj_object_test.rb +221 -187
  88. data/test/prime_meridian_test.rb +75 -75
  89. data/test/proj_test.rb +58 -58
  90. data/test/projection_test.rb +680 -650
  91. data/test/session_test.rb +78 -77
  92. data/test/transformation_test.rb +238 -210
  93. data/test/unit_test.rb +114 -76
  94. metadata +44 -32
  95. data/ChangeLog +0 -94
  96. data/README.rdoc +0 -189
  97. data/lib/api/api.rb +0 -117
  98. data/lib/api/api_5_0.rb +0 -338
  99. data/lib/api/api_5_1.rb +0 -7
  100. data/lib/api/api_5_2.rb +0 -5
  101. data/lib/api/api_6_0.rb +0 -146
  102. data/lib/api/api_6_1.rb +0 -5
  103. data/lib/api/api_6_2.rb +0 -10
  104. data/lib/api/api_6_3.rb +0 -6
  105. data/lib/api/api_7_0.rb +0 -69
  106. data/lib/api/api_7_1.rb +0 -73
  107. data/lib/api/api_7_2.rb +0 -14
  108. data/lib/api/api_8_0.rb +0 -6
  109. data/lib/api/api_8_1.rb +0 -24
  110. data/lib/api/api_8_2.rb +0 -6
  111. data/lib/api/api_9_1.rb +0 -7
  112. data/lib/api/api_9_2.rb +0 -9
  113. data/lib/api/api_9_4.rb +0 -6
  114. data/lib/api/api_experimental.rb +0 -201
  115. data/lib/proj/file_api.rb +0 -166
  116. data/lib/proj/network_api.rb +0 -92
@@ -0,0 +1,7 @@
1
+ # Generated by ruby-bindgen (1.0.0)
2
+
3
+ module Proj
4
+ module Api
5
+
6
+ end
7
+ end
@@ -0,0 +1,47 @@
1
+ # Generated by ruby-bindgen (1.0.0)
2
+
3
+ require 'ffi'
4
+
5
+ module Proj
6
+ module Api
7
+ extend FFI::Library
8
+
9
+ def self.library_names
10
+ ["proj"]
11
+ end
12
+
13
+ def self.library_versions
14
+ ["9_1", "9", "25", "22", "19", "17", "15", "14", "13", "12", "11"]
15
+ end
16
+
17
+ def self.search_names
18
+ result = Array.new
19
+ self.library_names.each do |name|
20
+ result << "lib#{name}"
21
+ self.library_versions.each do |version|
22
+ case RbConfig::CONFIG['host_os']
23
+ when /darwin|mac os/
24
+ result << "lib#{name}.#{version}"
25
+ when /mingw/
26
+ result << "lib#{name}-#{version}"
27
+ when /mswin/
28
+ result << "#{name}_#{version}"
29
+ else
30
+ result << "lib#{name}.so.#{version}"
31
+ end
32
+ end
33
+ end
34
+ result
35
+ end
36
+
37
+ if ENV['PROJ_LIB_PATH']
38
+ ffi_lib self.search_names.map { |name| File.join(ENV['PROJ_LIB_PATH'], name) } + self.search_names
39
+ else
40
+ ffi_lib self.search_names
41
+ end
42
+ end
43
+ end
44
+
45
+ require_relative 'proj_version'
46
+ require_relative './proj'
47
+ require_relative './proj_experimental'
@@ -0,0 +1,26 @@
1
+ # Generated by ruby-bindgen (1.0.0)
2
+
3
+ module Proj
4
+ module Api
5
+ class PjInfo < FFI::Struct
6
+ layout :major, :int,
7
+ :minor, :int,
8
+ :patch, :int,
9
+ :release, :string,
10
+ :version, :string,
11
+ :searchpath, :string,
12
+ :paths, :pointer,
13
+ :path_count, :size_t
14
+ end
15
+
16
+ attach_function :proj_info, :proj_info, [], PjInfo.by_value
17
+
18
+ def self.proj_version
19
+ info = proj_info
20
+ info[:major] * 10000 + info[:minor] * 100 + info[:patch]
21
+ end
22
+
23
+ info = proj_info
24
+ Proj::Api::PROJ_VERSION = Gem::Version.new("#{info[:major]}.#{info[:minor]}.#{info[:patch]}")
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proj'
4
+
5
+ transform = Proj::Transformation.new('EPSG:4326', 'EPSG:3857')
6
+ normalized = transform.normalize_for_visualization
7
+
8
+ coord = Proj::Coordinate.new(x: -122.4194, y: 37.7749)
9
+ result = normalized.forward(coord)
10
+
11
+ raise 'normalization transform failed' unless result.x.finite? && result.y.finite?
12
+
13
+ puts "ok: web mercator x=#{result.x}, y=#{result.y}"
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proj'
4
+
5
+ # Create a UTM zone 32 conversion.
6
+ conversion = Proj::Conversion.new('+proj=utm +zone=32 +ellps=GRS80')
7
+
8
+ # Build an array of coordinates (lon/lat in radians).
9
+ coordinates = [
10
+ Proj::Coordinate.new(lon: Proj.degrees_to_radians(12), lat: Proj.degrees_to_radians(55), z: 45),
11
+ Proj::Coordinate.new(lon: Proj.degrees_to_radians(12), lat: Proj.degrees_to_radians(56), z: 50),
12
+ Proj::Coordinate.new(lon: Proj.degrees_to_radians(13), lat: Proj.degrees_to_radians(55), z: 30)
13
+ ]
14
+
15
+ # Transform all at once (more efficient than looping).
16
+ results = conversion.transform_array(coordinates, :PJ_FWD)
17
+
18
+ results.each_with_index do |coord, i|
19
+ puts "Point #{i}: x=#{coord.x.round(2)}, y=#{coord.y.round(2)}, z=#{coord.z}"
20
+ end
21
+
22
+ raise 'wrong count' unless results.size == 3
23
+ raise 'bad transform' unless results.all? { |c| c.x.finite? && c.y.finite? }
24
+
25
+ puts 'ok'
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proj'
4
+
5
+ # Create a dedicated context so we don't affect the default.
6
+ context = Proj::Context.new
7
+
8
+ # Collect log messages into an array.
9
+ messages = []
10
+
11
+ context.set_log_function do |_pointer, level, message|
12
+ messages << "[level=#{level}] #{message}"
13
+ end
14
+
15
+ # Trigger a log message by setting an invalid database path.
16
+ begin
17
+ context.database.path = '/nonexistent'
18
+ rescue Proj::Error
19
+ # Expected - we just want to see the log output.
20
+ end
21
+
22
+ messages.each { |msg| puts msg }
23
+
24
+ raise 'no log messages captured' if messages.empty?
25
+
26
+ puts 'ok'
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proj'
4
+
5
+ # Start with a CRS created from a well-known string.
6
+ crs = Proj::Crs.new('OGC:CRS84')
7
+
8
+ # Identify it against the OGC authority.
9
+ objects, confidences = crs.identify('OGC')
10
+
11
+ objects.count.times do |i|
12
+ puts "Match: #{objects[i]} (confidence: #{confidences[i]}%)"
13
+ end
14
+
15
+ raise 'no matches' if objects.count.zero?
16
+ raise 'low confidence' if confidences[0] < 100
17
+
18
+ puts 'ok'
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proj'
4
+
5
+ database = Proj::Database.new(Proj::Context.current)
6
+
7
+ # List all authorities in the database.
8
+ authorities = database.authorities
9
+ puts "Authorities: #{authorities.to_a.first(5).join(', ')} (#{authorities.count} total)"
10
+
11
+ # Count EPSG coordinate reference systems.
12
+ codes = database.codes('EPSG', :PJ_TYPE_GEOGRAPHIC_2D_CRS)
13
+ puts "EPSG geographic 2D CRS codes: #{codes.count}"
14
+
15
+ # Query CRS entries for a specific authority.
16
+ crs_infos = database.crs_info('EPSG')
17
+ puts "EPSG CRS entries: #{crs_infos.count}"
18
+
19
+ # Show the first entry.
20
+ info = crs_infos.first
21
+ puts "First: #{info.auth_name}:#{info.code} - #{info.name} (#{info.crs_type})"
22
+ puts " Area: #{info.area_name}"
23
+ puts " Bounds: W=#{info.west_lon_degree}, S=#{info.south_lat_degree}, E=#{info.east_lon_degree}, N=#{info.north_lat_degree}"
24
+
25
+ raise 'no CRS info found' if crs_infos.empty?
26
+
27
+ puts 'ok'
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proj'
4
+
5
+ # Use WGS 84 as the ellipsoid for distance calculations.
6
+ crs = Proj::Crs.new('EPSG:4326')
7
+
8
+ # Paris (lon, lat) and Berlin (lon, lat) in radians.
9
+ paris = Proj::Coordinate.new(x: Proj.degrees_to_radians(2.3522),
10
+ y: Proj.degrees_to_radians(48.8566))
11
+ berlin = Proj::Coordinate.new(x: Proj.degrees_to_radians(13.4050),
12
+ y: Proj.degrees_to_radians(52.5200))
13
+
14
+ # Simple geodesic distance on the ellipsoid.
15
+ distance = crs.lp_distance(paris, berlin)
16
+ raise "unexpected distance #{distance}" unless (distance - 878_000).abs < 5_000
17
+
18
+ puts "Distance Paris -> Berlin: #{(distance / 1000).round(1)} km"
19
+
20
+ # Full geodesic: distance + forward/reverse azimuth.
21
+ # Returns distance (meters) in x, forward azimuth (degrees) in y, reverse azimuth (degrees) in z.
22
+ result = crs.geod_distance(paris, berlin)
23
+ puts "Geodesic distance: #{(result.x / 1000).round(1)} km"
24
+ puts "Forward azimuth: #{result.y.round(2)} degrees"
25
+ puts "Reverse azimuth: #{result.z.round(2)} degrees"
26
+
27
+ # Geodesic direct problem (PROJ 9.7+): given a start point, azimuth, and distance,
28
+ # compute the endpoint.
29
+ if Proj::Api::PROJ_VERSION >= Gem::Version.new('9.7.0')
30
+ # From Paris, azimuth 0 (due north), 100 km.
31
+ endpoint = crs.geod_direct(paris, 0.0, 100_000)
32
+ puts "100 km north of Paris: lon=#{Proj.radians_to_degrees(endpoint.x).round(4)}, lat=#{Proj.radians_to_degrees(endpoint.y).round(4)}"
33
+ raise 'geod_direct failed' unless endpoint.x.finite? && endpoint.y.finite?
34
+ else
35
+ puts "geod_direct requires PROJ 9.7+ (have #{Proj::Api::PROJ_VERSION})"
36
+ end
37
+
38
+ puts 'ok'
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proj'
4
+
5
+ context = Proj::Context.new
6
+ crs_projected = Proj::Crs.new('+proj=utm +zone=32 +datum=WGS84 +type=crs', context)
7
+ crs_geodetic = crs_projected.geodetic_crs
8
+ transform = Proj::Transformation.new(crs_geodetic, crs_projected, context)
9
+
10
+ from = Proj::Coordinate.new(lon: 12.0, lat: 55.0)
11
+ to = transform.forward(from)
12
+ back = transform.inverse(to)
13
+
14
+ raise 'forward failed' unless to.x.finite? && to.y.finite?
15
+ raise 'inverse mismatch lon' unless (back.lon - 12.0).abs < 1e-8
16
+ raise 'inverse mismatch lat' unless (back.lat - 55.0).abs < 1e-8
17
+
18
+ puts "ok: projected x=#{to.x}, y=#{to.y}"
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proj'
4
+
5
+ context = Proj::Context.new
6
+ source = Proj::Crs.create_from_database('EPSG', '4267', :PJ_CATEGORY_CRS)
7
+ target = Proj::Crs.create_from_database('EPSG', '4269', :PJ_CATEGORY_CRS)
8
+
9
+ factory = Proj::OperationFactoryContext.new(context)
10
+ factory.spatial_criterion = :PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION
11
+ factory.grid_availability = :PROJ_GRID_AVAILABILITY_IGNORED
12
+
13
+ operations = factory.create_operations(source, target)
14
+ raise 'no operations found' if operations.count.zero?
15
+
16
+ index = operations.suggested_operation(:PJ_FWD, Proj::Coordinate.new(x: 40, y: -100))
17
+ raise 'invalid suggested operation index' if index.negative? || index >= operations.count
18
+
19
+ puts "ok: best operation #{index} => #{operations[index].name}"
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proj'
4
+
5
+ conversion = Proj::Conversion.new(<<~EOS)
6
+ +proj=pipeline
7
+ +step +inv +proj=lcc +lat_1=33.88333333333333
8
+ +lat_2=32.78333333333333 +lat_0=32.16666666666666
9
+ +lon_0=-116.25 +x_0=2000000.0001016 +y_0=500000.0001016001 +ellps=GRS80
10
+ +towgs84=0,0,0,0,0,0,0 +units=us-ft +no_defs
11
+ +step +proj=lcc +lat_1=33.88333333333333 +lat_2=32.78333333333333 +lat_0=32.16666666666666
12
+ +lon_0=-116.25 +x_0=2000000 +y_0=500000
13
+ +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs
14
+ EOS
15
+
16
+ from = Proj::Coordinate.new(x: 4_760_096.421921, y: 3_744_293.729449)
17
+ to = conversion.forward(from)
18
+
19
+ raise 'pipeline conversion failed' unless to.x.finite? && to.y.finite?
20
+
21
+ puts "ok: converted x=#{to.x}, y=#{to.y}"
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proj'
4
+
5
+ # Start with a 2D geographic CRS.
6
+ crs_2d = Proj::Crs.new('EPSG:4326')
7
+ puts "Original: #{crs_2d.name}"
8
+ puts " Type: #{crs_2d.proj_type}"
9
+
10
+ # Promote to 3D (adds an ellipsoidal height axis).
11
+ crs_3d = crs_2d.promote_to_3d
12
+ puts "Promoted: #{crs_3d.name}"
13
+ puts " Type: #{crs_3d.proj_type}"
14
+
15
+ # Demote back to 2D.
16
+ crs_back = crs_3d.demote_to_2d
17
+ puts "Demoted: #{crs_back.name}"
18
+ puts " Type: #{crs_back.proj_type}"
19
+
20
+ raise 'promote failed' unless crs_3d.proj_type == :PJ_TYPE_GEOGRAPHIC_3D_CRS
21
+ raise 'demote failed' unless crs_back.proj_type == :PJ_TYPE_GEOGRAPHIC_2D_CRS
22
+
23
+ puts 'ok'
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'proj'
5
+
6
+ crs = Proj::Crs.new('EPSG:4326')
7
+ wkt = crs.to_wkt(:PJ_WKT2_2019, multiline: false)
8
+ projjson = crs.to_json(multiline: false)
9
+ proj_str = crs.to_proj_string
10
+
11
+ raise 'wkt missing' if wkt.nil? || wkt.empty?
12
+ raise 'proj string missing' if proj_str.nil? || proj_str.empty?
13
+
14
+ parsed = JSON.parse(projjson)
15
+ raise 'projjson parse failed' unless parsed['type']
16
+
17
+ puts "ok: serialized #{crs.auth}"
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proj'
4
+
5
+ # Transform a bounding box from geographic to projected coordinates.
6
+ transform = Proj::Transformation.new('EPSG:4326',
7
+ '+proj=laea +lat_0=45 +lon_0=-100 +x_0=0 +y_0=0 +a=6370997 +b=6370997 +units=m +no_defs')
8
+
9
+ # Bounding box covering parts of North America (lat_min, lon_min, lat_max, lon_max).
10
+ bounds = Proj::Bounds.new(40, -120, 64, -80)
11
+ result = transform.transform_bounds(bounds, :PJ_FWD, 21)
12
+
13
+ puts "Input: xmin=#{bounds.xmin}, ymin=#{bounds.ymin}, xmax=#{bounds.xmax}, ymax=#{bounds.ymax}"
14
+ puts "Output: xmin=#{result.xmin.round(2)}, ymin=#{result.ymin.round(2)}, xmax=#{result.xmax.round(2)}, ymax=#{result.ymax.round(2)}"
15
+
16
+ raise 'transform_bounds failed' unless result.xmin.finite? && result.ymax.finite?
17
+
18
+ puts 'ok'
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'proj'
4
+
5
+ area = Proj::Area.new(
6
+ west_lon_degree: -114.1324,
7
+ south_lat_degree: 49.5614,
8
+ east_lon_degree: 3.76488,
9
+ north_lat_degree: 62.1463
10
+ )
11
+
12
+ transform = Proj::Transformation.new('EPSG:4277', 'EPSG:4326', area: area)
13
+ from = Proj::Coordinate.new(x: 50, y: -2)
14
+ to = transform.forward(from)
15
+
16
+ raise 'area transformation failed' unless to.x.finite? && to.y.finite?
17
+
18
+ puts "ok: transformed x=#{to.x}, y=#{to.y}"
data/lib/proj/area.rb CHANGED
@@ -1,74 +1,74 @@
1
- # encoding: UTF-8
2
-
3
- module Proj
4
- # Areas are used to specify the area of use for the choice of relevant coordinate operations.
5
- # See Transformation#new
6
- class Area
7
- attr_reader :name, :west_lon_degree, :south_lat_degree, :east_lon_degree, :north_lat_degree
8
-
9
- # @!visibility private
10
- def self.finalize(pointer)
11
- proc do
12
- Api.proj_area_destroy(pointer)
13
- end
14
- end
15
-
16
- def initialize(west_lon_degree:, south_lat_degree:, east_lon_degree:, north_lat_degree:, name: nil)
17
- @west_lon_degree = west_lon_degree
18
- @south_lat_degree = south_lat_degree
19
- @east_lon_degree = east_lon_degree
20
- @north_lat_degree = north_lat_degree
21
- @name = name
22
- create_area
23
- end
24
-
25
- def to_ptr
26
- @area
27
- end
28
-
29
- # Sets the bounds for an area
30
- #
31
- # @see https://proj.org/development/reference/functions.html#c.proj_area_set_bbox
32
- #
33
- # @param west_lon_degree [Float] West longitude, in degrees. In [-180,180] range.
34
- # @param south_lat_degree [Float] South latitude, in degrees. In [-90,90] range.
35
- # @param east_lon_degree [Float] East longitude, in degrees. In [-180,180] range.
36
- # @param north_lat_degree [Float] North latitude, in degrees. In [-90,90] range.
37
- def set_bounds(west_lon_degree:, south_lat_degree:, east_lon_degree:, north_lat_degree:)
38
- Api.proj_area_set_bbox(self, west_lon_degree, south_lat_degree, east_lon_degree, north_lat_degree)
39
- end
40
-
41
- # Sets the name for an area
42
- #
43
- # @param value [String] The name of the area
44
- def name=(value)
45
- @name = name
46
- # This Api wasn't added until proj 9.1
47
- if defined?(Api.proj_area_set_name)
48
- Api.proj_area_set_name(self, value)
49
- end
50
- end
51
-
52
- # Returns nice printout of an Area
53
- #
54
- # @return [String]
55
- def to_s
56
- "Area west_lon_degree: #{self.west_lon_degree}, south_lat_degree: #{self.south_lat_degree}, east_lon_degree: #{self.east_lon_degree}, north_lat_degree: #{self.north_lat_degree}"
57
- end
58
-
59
- private
60
-
61
- # Creates an area
62
- #
63
- # @see https://proj.org/development/reference/functions.html#c.proj_area_create
64
- def create_area
65
- @area = Api.proj_area_create
66
- self.set_bounds(west_lon_degree: west_lon_degree, south_lat_degree: south_lat_degree,
67
- east_lon_degree: east_lon_degree, north_lat_degree: north_lat_degree)
68
- if name
69
- self.name = name
70
- end
71
- ObjectSpace.define_finalizer(self, self.class.finalize(@area))
72
- end
73
- end
74
- end
1
+ # encoding: UTF-8
2
+
3
+ module Proj
4
+ # Areas are used to specify the area of use for the choice of relevant coordinate operations.
5
+ # See Transformation#new
6
+ class Area
7
+ attr_reader :name, :west_lon_degree, :south_lat_degree, :east_lon_degree, :north_lat_degree
8
+
9
+ # @!visibility private
10
+ def self.finalize(pointer)
11
+ proc do
12
+ Api.proj_area_destroy(pointer)
13
+ end
14
+ end
15
+
16
+ def initialize(west_lon_degree:, south_lat_degree:, east_lon_degree:, north_lat_degree:, name: nil)
17
+ @west_lon_degree = west_lon_degree
18
+ @south_lat_degree = south_lat_degree
19
+ @east_lon_degree = east_lon_degree
20
+ @north_lat_degree = north_lat_degree
21
+ @name = name
22
+ create_area
23
+ end
24
+
25
+ def to_ptr
26
+ @area
27
+ end
28
+
29
+ # Sets the bounds for an area
30
+ #
31
+ # @see https://proj.org/development/reference/functions.html#c.proj_area_set_bbox
32
+ #
33
+ # @param west_lon_degree [Float] West longitude, in degrees. In [-180,180] range.
34
+ # @param south_lat_degree [Float] South latitude, in degrees. In [-90,90] range.
35
+ # @param east_lon_degree [Float] East longitude, in degrees. In [-180,180] range.
36
+ # @param north_lat_degree [Float] North latitude, in degrees. In [-90,90] range.
37
+ def set_bounds(west_lon_degree:, south_lat_degree:, east_lon_degree:, north_lat_degree:)
38
+ Api.proj_area_set_bbox(self, west_lon_degree, south_lat_degree, east_lon_degree, north_lat_degree)
39
+ end
40
+
41
+ # Sets the name for an area
42
+ #
43
+ # @param value [String] The name of the area
44
+ def name=(value)
45
+ @name = value
46
+ # This Api wasn't added until proj 9.1
47
+ if defined?(Api.proj_area_set_name)
48
+ Api.proj_area_set_name(self, value)
49
+ end
50
+ end
51
+
52
+ # Returns nice printout of an Area
53
+ #
54
+ # @return [String]
55
+ def to_s
56
+ "Area west_lon_degree: #{self.west_lon_degree}, south_lat_degree: #{self.south_lat_degree}, east_lon_degree: #{self.east_lon_degree}, north_lat_degree: #{self.north_lat_degree}"
57
+ end
58
+
59
+ private
60
+
61
+ # Creates an area
62
+ #
63
+ # @see https://proj.org/development/reference/functions.html#c.proj_area_create
64
+ def create_area
65
+ @area = Api.proj_area_create
66
+ self.set_bounds(west_lon_degree: west_lon_degree, south_lat_degree: south_lat_degree,
67
+ east_lon_degree: east_lon_degree, north_lat_degree: north_lat_degree)
68
+ if name
69
+ self.name = name
70
+ end
71
+ ObjectSpace.define_finalizer(self, self.class.finalize(@area))
72
+ end
73
+ end
74
+ end
@@ -1,44 +1,44 @@
1
- module Proj
2
- class AxisInfo
3
- # @!attribute [r] name
4
- # @return [String] Axis name
5
- # @!attribute [r] abbreviation
6
- # @return [String] Axis abbreviation
7
- # @!attribute [r] direction
8
- # @return [String] Axis direction
9
- # @!attribute [r] unit_conv_factor
10
- # @return [String] Axis unit_conv_factor
11
- # @!attribute [r] unit_name
12
- # @return [String] Axis unit_name
13
- # @!attribute [r] unit_auth_name
14
- # @return [String] Axis unit_auth_name
15
- # @!attribute [r] unit_code
16
- # @return [String] Axis unit_code
17
- attr_reader :name, :abbreviation, :direction,
18
- :unit_name, :unit_auth_name, :unit_code, :unit_conv_factor
19
-
20
- def initialize(name:, abbreviation:, direction:, unit_conv_factor:, unit_name:, unit_auth_name:, unit_code:)
21
- @name = name
22
- @abbreviation = abbreviation
23
- @direction = direction
24
- @unit_conv_factor = unit_conv_factor
25
- @unit_name = unit_name
26
- @unit_auth_name = unit_auth_name
27
- @unit_code = unit_code
28
- end
29
-
30
- # Returns axis information in PJ_AXIS_DESCRIPTION structure
31
- #
32
- # @return [PJ_AXIS_DESCRIPTION]
33
- def to_description
34
- Api::PJ_AXIS_DESCRIPTION.create(name: name, abbreviation: abbreviation, direction: direction,
35
- unit_conv_factor: unit_conv_factor, unit_name: name, unit_type: self.unit_type)
36
- end
37
-
38
- def unit_type
39
- database = Database.new(Context.default)
40
- unit = database.unit(self.unit_auth_name, self.unit_code)
41
- unit.unit_type
42
- end
43
- end
44
- end
1
+ module Proj
2
+ class AxisInfo
3
+ # @!attribute [r] name
4
+ # @return [String] Axis name
5
+ # @!attribute [r] abbreviation
6
+ # @return [String] Axis abbreviation
7
+ # @!attribute [r] direction
8
+ # @return [String] Axis direction
9
+ # @!attribute [r] unit_conv_factor
10
+ # @return [String] Axis unit_conv_factor
11
+ # @!attribute [r] unit_name
12
+ # @return [String] Axis unit_name
13
+ # @!attribute [r] unit_auth_name
14
+ # @return [String] Axis unit_auth_name
15
+ # @!attribute [r] unit_code
16
+ # @return [String] Axis unit_code
17
+ attr_reader :name, :abbreviation, :direction,
18
+ :unit_name, :unit_auth_name, :unit_code, :unit_conv_factor
19
+
20
+ def initialize(name:, abbreviation:, direction:, unit_conv_factor:, unit_name:, unit_auth_name:, unit_code:)
21
+ @name = name
22
+ @abbreviation = abbreviation
23
+ @direction = direction
24
+ @unit_conv_factor = unit_conv_factor
25
+ @unit_name = unit_name
26
+ @unit_auth_name = unit_auth_name
27
+ @unit_code = unit_code
28
+ end
29
+
30
+ # Returns axis information in PjAxisDescription structure
31
+ #
32
+ # @return [PjAxisDescription]
33
+ def to_description
34
+ Api::PjAxisDescription.create(name: name, abbreviation: abbreviation, direction: direction,
35
+ unit_conv_factor: unit_conv_factor, unit_name: unit_name, unit_type: self.unit_type)
36
+ end
37
+
38
+ def unit_type
39
+ database = Database.new(Context.default)
40
+ unit = database.unit(self.unit_auth_name, self.unit_code)
41
+ unit.unit_type
42
+ end
43
+ end
44
+ end
data/lib/proj/bounds.rb CHANGED
@@ -1,7 +1,29 @@
1
1
  module Proj
2
+ # Represents a 2D bounding box defined by minimum and maximum x/y values.
3
+ # Used with {CoordinateOperationMixin#transform_bounds} to transform rectangular
4
+ # regions between coordinate reference systems.
2
5
  class Bounds
6
+ # @!attribute [r] xmin
7
+ # @return [Float] Minimum x value (e.g., west longitude)
8
+ # @!attribute [r] ymin
9
+ # @return [Float] Minimum y value (e.g., south latitude)
10
+ # @!attribute [r] xmax
11
+ # @return [Float] Maximum x value (e.g., east longitude)
12
+ # @!attribute [r] ymax
13
+ # @return [Float] Maximum y value (e.g., north latitude)
14
+ # @!attribute [r] name
15
+ # @return [String, nil] Optional name for this bounds
3
16
  attr_reader :name, :xmin, :ymin, :xmax, :ymax
4
17
 
18
+ # Creates a new 2D bounding box.
19
+ #
20
+ # @param xmin [Float] Minimum x value
21
+ # @param ymin [Float] Minimum y value
22
+ # @param xmax [Float] Maximum x value
23
+ # @param ymax [Float] Maximum y value
24
+ # @param name [String, nil] Optional name
25
+ #
26
+ # @return [Bounds]
5
27
  def initialize(xmin, ymin, xmax, ymax, name = nil)
6
28
  @xmin = xmin
7
29
  @ymin = ymin