rgeo 2.3.1 → 3.0.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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -0
  3. data/README.md +23 -14
  4. data/ext/geos_c_impl/analysis.c +30 -25
  5. data/ext/geos_c_impl/analysis.h +8 -7
  6. data/ext/geos_c_impl/coordinates.c +27 -21
  7. data/ext/geos_c_impl/coordinates.h +5 -2
  8. data/ext/geos_c_impl/errors.c +19 -10
  9. data/ext/geos_c_impl/errors.h +11 -4
  10. data/ext/geos_c_impl/extconf.rb +42 -28
  11. data/ext/geos_c_impl/factory.c +540 -451
  12. data/ext/geos_c_impl/factory.h +105 -95
  13. data/ext/geos_c_impl/geometry.c +593 -387
  14. data/ext/geos_c_impl/geometry.h +10 -5
  15. data/ext/geos_c_impl/geometry_collection.c +306 -339
  16. data/ext/geos_c_impl/geometry_collection.h +6 -20
  17. data/ext/geos_c_impl/globals.c +169 -0
  18. data/ext/geos_c_impl/globals.h +46 -0
  19. data/ext/geos_c_impl/line_string.c +271 -231
  20. data/ext/geos_c_impl/line_string.h +5 -8
  21. data/ext/geos_c_impl/main.c +16 -16
  22. data/ext/geos_c_impl/point.c +65 -67
  23. data/ext/geos_c_impl/point.h +4 -7
  24. data/ext/geos_c_impl/polygon.c +137 -135
  25. data/ext/geos_c_impl/polygon.h +11 -11
  26. data/ext/geos_c_impl/preface.h +16 -10
  27. data/ext/geos_c_impl/ruby_more.c +67 -0
  28. data/ext/geos_c_impl/ruby_more.h +25 -0
  29. data/lib/rgeo/cartesian/analysis.rb +5 -3
  30. data/lib/rgeo/cartesian/bounding_box.rb +74 -79
  31. data/lib/rgeo/cartesian/calculations.rb +64 -33
  32. data/lib/rgeo/cartesian/factory.rb +57 -102
  33. data/lib/rgeo/cartesian/feature_classes.rb +68 -46
  34. data/lib/rgeo/cartesian/feature_methods.rb +67 -25
  35. data/lib/rgeo/cartesian/interface.rb +6 -41
  36. data/lib/rgeo/cartesian/planar_graph.rb +373 -0
  37. data/lib/rgeo/cartesian/sweepline_intersector.rb +147 -0
  38. data/lib/rgeo/cartesian/valid_op.rb +69 -0
  39. data/lib/rgeo/cartesian.rb +3 -0
  40. data/lib/rgeo/coord_sys/cs/entities.rb +303 -99
  41. data/lib/rgeo/coord_sys/cs/factories.rb +0 -2
  42. data/lib/rgeo/coord_sys/cs/wkt_parser.rb +90 -42
  43. data/lib/rgeo/coord_sys.rb +1 -20
  44. data/lib/rgeo/error.rb +15 -0
  45. data/lib/rgeo/feature/curve.rb +0 -11
  46. data/lib/rgeo/feature/factory.rb +26 -36
  47. data/lib/rgeo/feature/factory_generator.rb +6 -14
  48. data/lib/rgeo/feature/geometry.rb +146 -66
  49. data/lib/rgeo/feature/geometry_collection.rb +16 -9
  50. data/lib/rgeo/feature/line_string.rb +4 -5
  51. data/lib/rgeo/feature/linear_ring.rb +0 -1
  52. data/lib/rgeo/feature/multi_curve.rb +0 -6
  53. data/lib/rgeo/feature/multi_surface.rb +3 -4
  54. data/lib/rgeo/feature/point.rb +4 -5
  55. data/lib/rgeo/feature/polygon.rb +1 -2
  56. data/lib/rgeo/feature/surface.rb +3 -4
  57. data/lib/rgeo/feature/types.rb +69 -85
  58. data/lib/rgeo/geographic/factory.rb +98 -125
  59. data/lib/rgeo/geographic/interface.rb +69 -166
  60. data/lib/rgeo/geographic/projected_feature_classes.rb +21 -9
  61. data/lib/rgeo/geographic/projected_feature_methods.rb +67 -42
  62. data/lib/rgeo/geographic/projected_window.rb +36 -22
  63. data/lib/rgeo/geographic/{proj4_projector.rb → projector.rb} +3 -5
  64. data/lib/rgeo/geographic/simple_mercator_projector.rb +26 -25
  65. data/lib/rgeo/geographic/spherical_feature_classes.rb +29 -9
  66. data/lib/rgeo/geographic/spherical_feature_methods.rb +86 -9
  67. data/lib/rgeo/geographic/spherical_math.rb +17 -20
  68. data/lib/rgeo/geographic.rb +1 -1
  69. data/lib/rgeo/geos/capi_factory.rb +87 -158
  70. data/lib/rgeo/geos/capi_feature_classes.rb +50 -36
  71. data/lib/rgeo/geos/ffi_factory.rb +105 -173
  72. data/lib/rgeo/geos/ffi_feature_classes.rb +34 -10
  73. data/lib/rgeo/geos/ffi_feature_methods.rb +105 -127
  74. data/lib/rgeo/geos/interface.rb +20 -59
  75. data/lib/rgeo/geos/utils.rb +5 -5
  76. data/lib/rgeo/geos/zm_factory.rb +53 -95
  77. data/lib/rgeo/geos/zm_feature_methods.rb +30 -33
  78. data/lib/rgeo/geos.rb +8 -8
  79. data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +9 -22
  80. data/lib/rgeo/impl_helper/basic_geometry_methods.rb +1 -2
  81. data/lib/rgeo/impl_helper/basic_line_string_methods.rb +28 -56
  82. data/lib/rgeo/impl_helper/basic_point_methods.rb +2 -14
  83. data/lib/rgeo/impl_helper/basic_polygon_methods.rb +17 -26
  84. data/lib/rgeo/impl_helper/utils.rb +21 -0
  85. data/lib/rgeo/impl_helper/valid_op.rb +350 -0
  86. data/lib/rgeo/impl_helper/validity_check.rb +139 -0
  87. data/lib/rgeo/impl_helper.rb +1 -0
  88. data/lib/rgeo/version.rb +1 -1
  89. data/lib/rgeo/wkrep/wkb_generator.rb +73 -63
  90. data/lib/rgeo/wkrep/wkb_parser.rb +33 -31
  91. data/lib/rgeo/wkrep/wkt_generator.rb +52 -45
  92. data/lib/rgeo/wkrep/wkt_parser.rb +48 -35
  93. data/lib/rgeo.rb +1 -3
  94. metadata +50 -13
  95. data/lib/rgeo/coord_sys/srs_database/entry.rb +0 -107
  96. data/lib/rgeo/coord_sys/srs_database/sr_org.rb +0 -64
  97. data/lib/rgeo/coord_sys/srs_database/url_reader.rb +0 -65
@@ -134,12 +134,19 @@ module RGeo
134
134
  # Highest possible value for local datum types.
135
135
  LD_MAX = 32_767
136
136
 
137
+ # Flags indicating parts of domain covered by a convex hull. These flags can be combined. For
138
+ # example, the value 3 corresponds to a combination of CT_DF_Inside and MF_DF_Outside, which
139
+ # means that some parts of the convex hull are inside the domain, and some parts of the convex
140
+ # hull are outside the domain
141
+ CT_DF_INSIDE = 1
142
+ CT_DF_OUTSIDE = 2
143
+ CT_DF_DISCONTINUOUS = 4
144
+
137
145
  # This is a base class for all OGC coordinate system objects.
138
146
  # This includes both interfaces and data types from the OGC
139
147
  # Coordinate Transformation spec.
140
148
  #
141
149
  # This is a non-instantiable abstract class.
142
-
143
150
  class Base
144
151
  # Standard object inspection output
145
152
 
@@ -150,8 +157,8 @@ module RGeo
150
157
  # Tests for equality. Two objects are defined as equal if they
151
158
  # have the same type (class) and the same WKT representation.
152
159
 
153
- def eql?(rhs)
154
- rhs.class == self.class && rhs.to_wkt == to_wkt
160
+ def eql?(other)
161
+ other.class == self.class && other.to_wkt == to_wkt
155
162
  end
156
163
  alias == eql?
157
164
 
@@ -172,19 +179,21 @@ module RGeo
172
179
  # <tt>:standard_brackets</tt>
173
180
  # If true, outputs parentheses rather than square
174
181
  # brackets. Default is false.
175
- def to_wkt(standard_brackets = false)
182
+ def to_wkt(standard_brackets: false)
176
183
  open, close = brackets(standard_brackets)
177
184
  content = wkt_content(standard_brackets).map { |obj| ",#{obj}" }.join
178
- if defined?(@authority) && @authority
179
- authority = ",AUTHORITY#{open}#{@authority.inspect},#{@authority_code.inspect}#{close}"
180
- else
181
- authority = ""
182
- end
183
- if defined?(@extensions) && @extensions
184
- extensions = @extensions.map { |k, v| ",EXTENSION#{open}#{k.inspect},#{v.inspect}#{close}" }.join
185
- else
186
- extensions = ""
187
- end
185
+ authority =
186
+ if defined?(@authority) && @authority
187
+ ",AUTHORITY#{open}#{@authority.inspect},#{@authority_code.inspect}#{close}"
188
+ else
189
+ ""
190
+ end
191
+ extensions =
192
+ if defined?(@extensions) && @extensions
193
+ @extensions.map { |k, v| ",EXTENSION#{open}#{k.inspect},#{v.inspect}#{close}" }.join
194
+ else
195
+ ""
196
+ end
188
197
  "#{wkt_typename}#{open}#{@name.inspect}#{content}#{extensions}#{authority}#{close}"
189
198
  end
190
199
 
@@ -197,12 +206,11 @@ module RGeo
197
206
  def marshal_load(data) # :nodoc:
198
207
  data = data["wkt"] if data.is_a?(Hash)
199
208
  temp = CS.create_from_wkt(data)
200
- if temp.class == self.class
201
- temp.instance_variables.each do |iv|
202
- instance_variable_set(iv, temp.instance_variable_get(iv))
203
- end
204
- else
205
- raise TypeError, "Bad Marshal data"
209
+
210
+ raise TypeError, "Bad Marshal data" unless temp.instance_of?(self.class)
211
+
212
+ temp.instance_variables.each do |iv|
213
+ instance_variable_set(iv, temp.instance_variable_get(iv))
206
214
  end
207
215
  end
208
216
 
@@ -214,12 +222,11 @@ module RGeo
214
222
 
215
223
  def init_with(coder) # :nodoc:
216
224
  temp = CS.create_from_wkt(coder.type == :scalar ? coder.scalar : coder["wkt"])
217
- if temp.class == self.class
218
- temp.instance_variables.each do |iv|
219
- instance_variable_set(iv, temp.instance_variable_get(iv))
220
- end
221
- else
222
- raise TypeError, "Bad YAML data"
225
+
226
+ raise TypeError, "Bad YAML data" unless temp.instance_of?(self.class)
227
+
228
+ temp.instance_variables.each do |iv|
229
+ instance_variable_set(iv, temp.instance_variable_get(iv))
223
230
  end
224
231
  end
225
232
 
@@ -238,20 +245,21 @@ module RGeo
238
245
  #
239
246
  # Details of axis. This is used to label axes, and indicate the
240
247
  # orientation.
241
-
242
248
  class AxisInfo < Base
243
249
  # :stopdoc:
244
250
  NAMES_BY_VALUE = %w[OTHER NORTH SOUTH EAST WEST UP DOWN].freeze
245
251
  # :startdoc:
246
252
 
247
253
  def initialize(name, orientation) # :nodoc:
254
+ super()
248
255
  @name = name
249
- case orientation
250
- when String, Symbol
251
- @orientation = NAMES_BY_VALUE.index(orientation.to_s.upcase).to_i
252
- else
253
- @orientation = orientation.to_i
254
- end
256
+ @orientation =
257
+ case orientation
258
+ when String, Symbol
259
+ NAMES_BY_VALUE.index(orientation.to_s.upcase).to_i
260
+ else
261
+ orientation.to_i
262
+ end
255
263
  end
256
264
 
257
265
  # Human readable name for axis. Possible values are "X", "Y",
@@ -293,9 +301,9 @@ module RGeo
293
301
  # projected coordinate system. The angular units of parameter
294
302
  # values match the angular units of the geographic coordinate
295
303
  # system that the projected coordinate system is based on.
296
-
297
304
  class ProjectionParameter < Base
298
305
  def initialize(name, value) # :nodoc:
306
+ super()
299
307
  @name = name
300
308
  @value = value.to_f
301
309
  end
@@ -331,15 +339,15 @@ module RGeo
331
339
  # Wolf parameters should be applied to geocentric coordinates, where
332
340
  # the X axis points towards the Greenwich Prime Meridian, the Y axis
333
341
  # points East, and the Z axis points North.
334
-
335
342
  class WGS84ConversionInfo < Base
336
- def initialize(dx, dy, dz, ex, ey, ez, ppm) # :nodoc:
337
- @dx = dx.to_f
338
- @dy = dy.to_f
339
- @dz = dz.to_f
340
- @ex = ex.to_f
341
- @ey = ey.to_f
342
- @ez = ez.to_f
343
+ def initialize(dx_meters, dy_meters, dz_meters, ex_arc_seconds, ey_arc_seconds, ez_arc_seconds, ppm) # :nodoc:
344
+ super()
345
+ @dx = dx_meters.to_f
346
+ @dy = dy_meters.to_f
347
+ @dz = dz_meters.to_f
348
+ @ex = ex_arc_seconds.to_f
349
+ @ey = ey_arc_seconds.to_f
350
+ @ez = ez_arc_seconds.to_f
343
351
  @ppm = ppm.to_f
344
352
  end
345
353
 
@@ -364,7 +372,7 @@ module RGeo
364
372
  # Bursa Wolf scaling in in parts per million.
365
373
  attr_reader :ppm
366
374
 
367
- def to_wkt(standard_brackets = false)
375
+ def to_wkt(standard_brackets: false)
368
376
  open, close = brackets(standard_brackets)
369
377
  "TOWGS84#{open}#{@dx},#{@dy},#{@dz},#{@ex},#{@ey},#{@ez},#{@ppm}#{close}"
370
378
  end
@@ -375,8 +383,8 @@ module RGeo
375
383
  # The Bursa Wolf shift should be in meters, the rotation in arc
376
384
  # seconds, and the scaling in parts per million.
377
385
 
378
- def create(dx, dy, dz, ex, ey, ez, ppm)
379
- new(dx, dy, dz, ex, ey, ez, ppm)
386
+ def create(dx_meters, dy_meters, dz_meters, ex_arc_seconds, ey_arc_seconds, ez_arc_seconds, ppm)
387
+ new(dx_meters, dy_meters, dz_meters, ex_arc_seconds, ey_arc_seconds, ez_arc_seconds, ppm)
380
388
  end
381
389
  end
382
390
  end
@@ -419,9 +427,10 @@ module RGeo
419
427
  # * <b>alias</b>: an alias
420
428
  # * <b>remarks</b>: provider-supplied remarks.
421
429
  # * <b>extensions</b>: a hash of extension keys and values
422
-
423
430
  class Info < Base
424
- def initialize(name, authority = nil, authority_code = nil, abbreviation = nil, init_alias = nil, remarks = nil, extensions = nil) # :nodoc:
431
+ def initialize(name, authority = nil, authority_code = nil, abbreviation = nil, init_alias = nil,
432
+ remarks = nil, extensions = nil) # :nodoc:
433
+ super()
425
434
  @name = name
426
435
  @authority = authority ? authority.to_s : nil
427
436
  @authority_code = authority_code ? authority_code.to_s : nil
@@ -480,7 +489,6 @@ module RGeo
480
489
  # Normally, you will instantiate one of the subclasses LinearUnit or
481
490
  # AngularUnit. However, it is possible to instantiate Unit if it is
482
491
  # not clear whether the data refers to a LinearUnit or AngularUnit.
483
-
484
492
  class Unit < Info
485
493
  def initialize(name, conversion_factor, *optional) # :nodoc:
486
494
  super(name, *optional)
@@ -517,7 +525,6 @@ module RGeo
517
525
  # == OGC spec description
518
526
  #
519
527
  # Definition of linear units.
520
-
521
528
  class LinearUnit < Unit
522
529
  # Returns the number of meters per LinearUnit.
523
530
  # Also available as Unit#conversion_factor.
@@ -540,7 +547,6 @@ module RGeo
540
547
  # == OGC spec description
541
548
  #
542
549
  # Definition of angular units.
543
-
544
550
  class AngularUnit < Unit
545
551
  # Returns the number of radians per AngularUnit.
546
552
  # Also available as Unit#conversion_factor.
@@ -563,7 +569,6 @@ module RGeo
563
569
  # == OGC spec description
564
570
  #
565
571
  # A meridian used to take longitude measurements from.
566
-
567
572
  class PrimeMeridian < Info
568
573
  def initialize(name, angular_unit, longitude, *optional) # :nodoc:
569
574
  super(name, *optional)
@@ -603,9 +608,9 @@ module RGeo
603
608
  # == OGC spec description
604
609
  #
605
610
  # An approximation of the Earth's surface as a squashed sphere.
606
-
607
611
  class Ellipsoid < Info
608
- def initialize(name, semi_major_axis, semi_minor_axis, inverse_flattening, ivf_definitive, linear_unit, *optional) # :nodoc:
612
+ def initialize(name, semi_major_axis, semi_minor_axis, inverse_flattening, ivf_definitive,
613
+ linear_unit, *optional) # :nodoc:
609
614
  super(name, *optional)
610
615
  @semi_major_axis = semi_major_axis.to_f
611
616
  @semi_minor_axis = semi_minor_axis.to_f
@@ -710,7 +715,6 @@ module RGeo
710
715
  # This is a non-instantiable abstract class. You must instantiate
711
716
  # one of the subclasses HorizontalDatum, VerticalDatum, or
712
717
  # LocalDatum.
713
-
714
718
  class Datum < Info
715
719
  def initialize(name, datum_type, *optional) # :nodoc:
716
720
  super(name, *optional)
@@ -730,7 +734,6 @@ module RGeo
730
734
  # == OGC spec description
731
735
  #
732
736
  # Procedure used to measure vertical distances.
733
-
734
737
  class VerticalDatum < Datum
735
738
  def wkt_typename
736
739
  "VERT_DATUM"
@@ -760,7 +763,6 @@ module RGeo
760
763
  # coordinates can be transformed between two different local
761
764
  # coordinate systems, as long as they are based on the same local
762
765
  # datum.
763
-
764
766
  class LocalDatum < Datum
765
767
  def wkt_typename
766
768
  "LOCAL_DATUM"
@@ -786,7 +788,6 @@ module RGeo
786
788
  # == OGC spec description
787
789
  #
788
790
  # Procedure used to measure positions on the surface of the Earth.
789
-
790
791
  class HorizontalDatum < Datum
791
792
  def initialize(name, datum_type, ellipsoid, wgs84_parameters, *optional) # :nodoc:
792
793
  super(name, datum_type, *optional)
@@ -820,8 +821,8 @@ module RGeo
820
821
  private
821
822
 
822
823
  def wkt_content(standard_brackets)
823
- array = [@ellipsoid.to_wkt(standard_brackets)]
824
- array << @wgs84_parameters.to_wkt(standard_brackets) if @wgs84_parameters
824
+ array = [@ellipsoid.to_wkt(standard_brackets: standard_brackets)]
825
+ array << @wgs84_parameters.to_wkt(standard_brackets: standard_brackets) if @wgs84_parameters
825
826
  array
826
827
  end
827
828
  end
@@ -829,7 +830,6 @@ module RGeo
829
830
  # == OGC spec description
830
831
  #
831
832
  # A projection from geographic coordinates to projected coordinates.
832
-
833
833
  class Projection < Info
834
834
  def initialize(name, class_name, parameters, *optional) # :nodoc:
835
835
  super(name, *optional)
@@ -908,7 +908,6 @@ module RGeo
908
908
  # GeographicCoordinateSystem, ProjectedCoordinateSystem,
909
909
  # VerticalCoordinateSystem, LocalCoordinateSystem, or
910
910
  # CompoundCoordinateSystem.
911
-
912
911
  class CoordinateSystem < Info
913
912
  def initialize(name, dimension, *optional) # :nodoc:
914
913
  super(name, *optional)
@@ -921,16 +920,61 @@ module RGeo
921
920
  # Gets axis details for dimension within coordinate system. Each
922
921
  # dimension in the coordinate system has a corresponding axis.
923
922
 
924
- def get_axis(dimension)
923
+ def get_axis(_dimension)
925
924
  nil
926
925
  end
927
926
 
928
927
  # Gets units for dimension within coordinate system. Each
929
928
  # dimension in the coordinate system has corresponding units.
930
929
 
931
- def get_units(dimension)
930
+ def get_units(_dimension)
932
931
  nil
933
932
  end
933
+
934
+ def geographic?
935
+ false
936
+ end
937
+
938
+ def projected?
939
+ false
940
+ end
941
+
942
+ def wkt_typename
943
+ "CS"
944
+ end
945
+
946
+ # Not an OGC method, but useful for being able to
947
+ # transform directly from a CoordinateSystem object.
948
+ def transform_coords(target_cs, x, y, z = nil)
949
+ ct = CoordinateTransform.create(self, target_cs)
950
+ ct.transform_coords(x, y, z)
951
+ end
952
+
953
+ class << self
954
+ def create(defn, dimension = 2, *optional)
955
+ # Need this so we can maintain consistency with actual
956
+ # CoordinateSystem implementations
957
+
958
+ if defn.is_a?(Integer)
959
+ # not technically correct but we can use cartesian as a placeholder
960
+ # to form valid wkt
961
+ defn_string = "Cartesian"
962
+ new(defn_string, dimension, "EPSG", defn, *optional)
963
+ else
964
+ new(defn, dimension, *optional)
965
+ end
966
+ end
967
+
968
+ def create_from_wkt(str)
969
+ CS.create_from_wkt(str)
970
+ end
971
+ end
972
+
973
+ private
974
+
975
+ def wkt_content(_)
976
+ [@dimension]
977
+ end
934
978
  end
935
979
 
936
980
  # == OGC spec description
@@ -940,7 +984,6 @@ module RGeo
940
984
  # as a geographic or a projected coordinate system with a horizontal
941
985
  # datum. The other is a vertical CRS which is a one-dimensional
942
986
  # coordinate system with a vertical datum.
943
-
944
987
  class CompoundCoordinateSystem < CoordinateSystem
945
988
  def initialize(name, head, tail, *optional) # :nodoc:
946
989
  super(name, head.dimension + tail.dimension, *optional)
@@ -985,7 +1028,7 @@ module RGeo
985
1028
  private
986
1029
 
987
1030
  def wkt_content(standard_brackets)
988
- [@head.to_wkt(standard_brackets), @tail.to_wkt(standard_brackets)]
1031
+ [@head.to_wkt(standard_brackets: standard_brackets), @tail.to_wkt(standard_brackets: standard_brackets)]
989
1032
  end
990
1033
  end
991
1034
 
@@ -1007,7 +1050,6 @@ module RGeo
1007
1050
  #
1008
1051
  # RGeo's implementation does not provide the Coordinate
1009
1052
  # Transformation (CT) package.
1010
-
1011
1053
  class LocalCoordinateSystem < CoordinateSystem
1012
1054
  def initialize(name, local_datum, unit, axes, *optional) # :nodoc:
1013
1055
  super(name, axes.size, *optional)
@@ -1027,7 +1069,7 @@ module RGeo
1027
1069
 
1028
1070
  # Implements CoordinateSystem#get_units
1029
1071
 
1030
- def get_units(index)
1072
+ def get_units(_index)
1031
1073
  @unit
1032
1074
  end
1033
1075
 
@@ -1050,9 +1092,9 @@ module RGeo
1050
1092
 
1051
1093
  def wkt_content(standard_brackets)
1052
1094
  [
1053
- @local_datum.to_wkt(standard_brackets),
1054
- @unit.to_wkt(standard_brackets)
1055
- ] + @axes.map { |ax| ax.to_wkt(standard_brackets) }
1095
+ @local_datum.to_wkt(standard_brackets: standard_brackets),
1096
+ @unit.to_wkt(standard_brackets: standard_brackets)
1097
+ ] + @axes.map { |ax| ax.to_wkt(standard_brackets: standard_brackets) }
1056
1098
  end
1057
1099
  end
1058
1100
 
@@ -1064,7 +1106,6 @@ module RGeo
1064
1106
  # the Z axis will point North, and the Y axis will point East (e.g.
1065
1107
  # a right handed system), but you should check the axes for
1066
1108
  # non-default values.
1067
-
1068
1109
  class GeocentricCoordinateSystem < CoordinateSystem
1069
1110
  def initialize(name, horizontal_datum, prime_meridian, linear_unit, axis0, axis1, axis2, *optional) # :nodoc:
1070
1111
  super(name, 3, *optional)
@@ -1090,7 +1131,7 @@ module RGeo
1090
1131
 
1091
1132
  # Implements CoordinateSystem#get_units
1092
1133
 
1093
- def get_units(index)
1134
+ def get_units(_index)
1094
1135
  @linear_unit
1095
1136
  end
1096
1137
 
@@ -1100,6 +1141,10 @@ module RGeo
1100
1141
  [@axis0, @axis1, @axis2][index]
1101
1142
  end
1102
1143
 
1144
+ def geographic?
1145
+ true
1146
+ end
1147
+
1103
1148
  def wkt_typename
1104
1149
  "GEOCCS"
1105
1150
  end
@@ -1120,13 +1165,13 @@ module RGeo
1120
1165
 
1121
1166
  def wkt_content(standard_brackets)
1122
1167
  arr = [
1123
- @horizontal_datum.to_wkt(standard_brackets),
1124
- @prime_meridian.to_wkt(standard_brackets),
1125
- @linear_unit.to_wkt(standard_brackets)
1168
+ @horizontal_datum.to_wkt(standard_brackets: standard_brackets),
1169
+ @prime_meridian.to_wkt(standard_brackets: standard_brackets),
1170
+ @linear_unit.to_wkt(standard_brackets: standard_brackets)
1126
1171
  ]
1127
- arr << @axis0.to_wkt(standard_brackets) if @axis0
1128
- arr << @axis1.to_wkt(standard_brackets) if @axis1
1129
- arr << @axis2.to_wkt(standard_brackets) if @axis2
1172
+ arr << @axis0.to_wkt(standard_brackets: standard_brackets) if @axis0
1173
+ arr << @axis1.to_wkt(standard_brackets: standard_brackets) if @axis1
1174
+ arr << @axis2.to_wkt(standard_brackets: standard_brackets) if @axis2
1130
1175
  arr
1131
1176
  end
1132
1177
  end
@@ -1135,7 +1180,6 @@ module RGeo
1135
1180
  #
1136
1181
  # A one-dimensional coordinate system suitable for vertical
1137
1182
  # measurements.
1138
-
1139
1183
  class VerticalCoordinateSystem < CoordinateSystem
1140
1184
  def initialize(name, vertical_datum, vertical_unit, axis, *optional) # :nodoc:
1141
1185
  super(name, 1, *optional)
@@ -1153,13 +1197,13 @@ module RGeo
1153
1197
 
1154
1198
  # Implements CoordinateSystem#get_units
1155
1199
 
1156
- def get_units(index)
1200
+ def get_units(_index)
1157
1201
  @vertical_unit
1158
1202
  end
1159
1203
 
1160
1204
  # Implements CoordinateSystem#get_axis
1161
1205
 
1162
- def get_axis(index)
1206
+ def get_axis(_index)
1163
1207
  @axis
1164
1208
  end
1165
1209
 
@@ -1181,8 +1225,11 @@ module RGeo
1181
1225
  private
1182
1226
 
1183
1227
  def wkt_content(standard_brackets)
1184
- arr = [@vertical_datum.to_wkt(standard_brackets), @vertical_unit.to_wkt(standard_brackets)]
1185
- arr << @axis.to_wkt(standard_brackets) if @axis
1228
+ arr = [
1229
+ @vertical_datum.to_wkt(standard_brackets: standard_brackets),
1230
+ @vertical_unit.to_wkt(standard_brackets: standard_brackets)
1231
+ ]
1232
+ arr << @axis.to_wkt(standard_brackets: standard_brackets) if @axis
1186
1233
  arr
1187
1234
  end
1188
1235
  end
@@ -1196,7 +1243,6 @@ module RGeo
1196
1243
  # This is a non-instantiable abstract class. You must instantiate
1197
1244
  # one of the subclasses GeographicCoordinateSystem or
1198
1245
  # ProjectedCoordinateSystem.
1199
-
1200
1246
  class HorizontalCoordinateSystem < CoordinateSystem
1201
1247
  def initialize(name, horizontal_datum, *optional) # :nodoc:
1202
1248
  super(name, 2, *optional)
@@ -1214,7 +1260,6 @@ module RGeo
1214
1260
  # You can find out which this is by examining the axes. You should
1215
1261
  # also check the angular units, since not all geographic coordinate
1216
1262
  # systems use degrees.
1217
-
1218
1263
  class GeographicCoordinateSystem < HorizontalCoordinateSystem
1219
1264
  def initialize(name, angular_unit, horizontal_datum, prime_meridian, axis0, axis1, *optional) # :nodoc:
1220
1265
  super(name, horizontal_datum, *optional)
@@ -1233,7 +1278,7 @@ module RGeo
1233
1278
 
1234
1279
  # Implements CoordinateSystem#get_units
1235
1280
 
1236
- def get_units(index)
1281
+ def get_units(_index)
1237
1282
  @angular_unit
1238
1283
  end
1239
1284
 
@@ -1255,10 +1300,14 @@ module RGeo
1255
1300
  # of interest. The first conversion (with index=0) should provide
1256
1301
  # acceptable accuracy over the largest possible area of interest.
1257
1302
 
1258
- def get_wgs84_conversion_info(index)
1303
+ def get_wgs84_conversion_info(_index)
1259
1304
  @horizontal_datum.wgs84_parameters
1260
1305
  end
1261
1306
 
1307
+ def geographic?
1308
+ true
1309
+ end
1310
+
1262
1311
  def wkt_typename
1263
1312
  "GEOGCS"
1264
1313
  end
@@ -1279,12 +1328,12 @@ module RGeo
1279
1328
 
1280
1329
  def wkt_content(standard_brackets)
1281
1330
  arr = [
1282
- @horizontal_datum.to_wkt(standard_brackets),
1283
- @prime_meridian.to_wkt(standard_brackets),
1284
- @angular_unit.to_wkt(standard_brackets)
1331
+ @horizontal_datum.to_wkt(standard_brackets: standard_brackets),
1332
+ @prime_meridian.to_wkt(standard_brackets: standard_brackets),
1333
+ @angular_unit.to_wkt(standard_brackets: standard_brackets)
1285
1334
  ]
1286
- arr << @axis0.to_wkt(standard_brackets) if @axis0
1287
- arr << @axis1.to_wkt(standard_brackets) if @axis1
1335
+ arr << @axis0.to_wkt(standard_brackets: standard_brackets) if @axis0
1336
+ arr << @axis1.to_wkt(standard_brackets: standard_brackets) if @axis1
1288
1337
  arr
1289
1338
  end
1290
1339
  end
@@ -1292,7 +1341,6 @@ module RGeo
1292
1341
  # == OGC spec description
1293
1342
  #
1294
1343
  # A 2D cartographic coordinate system.
1295
-
1296
1344
  class ProjectedCoordinateSystem < HorizontalCoordinateSystem
1297
1345
  def initialize(name, geographic_coordinate_system, projection, linear_unit, axis0, axis1, *optional) # :nodoc:
1298
1346
  super(name, geographic_coordinate_system.horizontal_datum, *optional)
@@ -1315,7 +1363,7 @@ module RGeo
1315
1363
 
1316
1364
  # Implements CoordinateSystem#get_units
1317
1365
 
1318
- def get_units(index)
1366
+ def get_units(_index)
1319
1367
  @linear_unit
1320
1368
  end
1321
1369
 
@@ -1325,6 +1373,10 @@ module RGeo
1325
1373
  index == 1 ? @axis1 : @axis0
1326
1374
  end
1327
1375
 
1376
+ def projected?
1377
+ true
1378
+ end
1379
+
1328
1380
  def wkt_typename
1329
1381
  "PROJCS"
1330
1382
  end
@@ -1344,14 +1396,166 @@ module RGeo
1344
1396
  private
1345
1397
 
1346
1398
  def wkt_content(standard_brackets)
1347
- arr = [@geographic_coordinate_system.to_wkt(standard_brackets), @projection.to_wkt(standard_brackets)]
1348
- @projection.each_parameter { |param| arr << param.to_wkt(standard_brackets) }
1349
- arr << @linear_unit.to_wkt(standard_brackets)
1350
- arr << @axis0.to_wkt(standard_brackets) if @axis0
1351
- arr << @axis1.to_wkt(standard_brackets) if @axis1
1399
+ arr = [
1400
+ @geographic_coordinate_system.to_wkt(standard_brackets: standard_brackets),
1401
+ @projection.to_wkt(standard_brackets: standard_brackets)
1402
+ ]
1403
+ @projection.each_parameter { |param| arr << param.to_wkt(standard_brackets: standard_brackets) }
1404
+ arr << @linear_unit.to_wkt(standard_brackets: standard_brackets)
1405
+ arr << @axis0.to_wkt(standard_brackets: standard_brackets) if @axis0
1406
+ arr << @axis1.to_wkt(standard_brackets: standard_brackets) if @axis1
1352
1407
  arr
1353
1408
  end
1354
1409
  end
1410
+
1411
+ # CoordinateTransform object. Note it is a combo of
1412
+ # CoordinateTransform and MathTransform as specified in
1413
+ # the OGC standard. This is just to simplify the model
1414
+ # and keep all functionality in this class.
1415
+ #
1416
+ # @see https://portal.ogc.org/files/?artifact_id=999 page 79
1417
+ class CoordinateTransform < Info
1418
+ # Initialize a new CoordinateTransform
1419
+ #
1420
+ # Note this class should not be used directly since it does not
1421
+ # implement any transformation logic. It merely defines
1422
+ # what methods actual implementations must use.
1423
+ #
1424
+ # @param [CoordinateSystem] source_cs
1425
+ # @param [CoordinateSystem] target_cs
1426
+ # @param [Array] optional any params for Info or Base
1427
+ # @return [CoordinateTransform]
1428
+ def initialize(source_cs, target_cs, *optional)
1429
+ super(optional)
1430
+ @source_cs = source_cs
1431
+ @target_cs = target_cs
1432
+ end
1433
+ attr_accessor :source_cs, :target_cs
1434
+
1435
+ # TODO: This changes depending on what type of conversion is done
1436
+ # and we can't know unless we implement the conversion ourselves.
1437
+ # We should delegate all of the wkt generation to the library
1438
+ # if possible.
1439
+ def wkt_typename
1440
+ "CONVERSION"
1441
+ end
1442
+
1443
+ def inspect
1444
+ "#<#{self.class}:0x#{object_id.to_s(16)} @source_cs=#{source_cs.to_wkt} @target_cs=#{target_cs.to_wkt}>"
1445
+ end
1446
+
1447
+ # Human readable description of domain in source coordinate system.
1448
+ #
1449
+ # @return [String]
1450
+ def area_of_use
1451
+ raise NotImplementedError, "#{__method__} is not implemented in the abstract CoordinateTransform class."
1452
+ end
1453
+
1454
+ # Semantic type of transform. For example, a datum transformation or a coordinate conversion.
1455
+ #
1456
+ # @return [String]
1457
+ def transform_type
1458
+ raise NotImplementedError, "#{__method__} is not implemented in the abstract CoordinateTransform class."
1459
+ end
1460
+
1461
+ # Dimension of the source_cs
1462
+ #
1463
+ # @return [Integer]
1464
+ def dim_source
1465
+ source_cs.dimension
1466
+ end
1467
+
1468
+ # Dimension of the target_cs
1469
+ #
1470
+ # @return [Integer]
1471
+ def dim_target
1472
+ target_cs.dimension
1473
+ end
1474
+
1475
+ # Tests whether this transform does not move any points
1476
+ #
1477
+ # @return [Boolean]
1478
+ def identity?
1479
+ raise NotImplementedError, "#{__method__} is not implemented in the abstract CoordinateTransform class."
1480
+ end
1481
+
1482
+ # Gets flags classifying domain points within a convex hull. The supplied ordinates are interpreted
1483
+ # as a sequence of points, which generates a convex hull in the source space. Conceptually, each
1484
+ # of the (usually infinite) points inside the convex hull is then tested against the source domain.
1485
+ # The flags of all these tests are then combined. In practice, implementations of different
1486
+ # transforms will use different short-cuts to avoid doing an infinite number of tests.
1487
+ #
1488
+ # @param [Array<<Array<Integer>>] points in tuples of (x,y,z) with z being optional
1489
+ # @return [Array<Integer>] the domain_flags of the input points
1490
+ def domain_flags(points)
1491
+ raise NotImplementedError, "#{__method__} is not implemented in the abstract CoordinateTransform class."
1492
+ end
1493
+
1494
+ # Gets transformed convex hull. The supplied ordinates are interpreted as a sequence of points,
1495
+ # which generates a convex hull in the source space. The returned sequence of ordinates
1496
+ # represents a convex hull in the output space. The number of output points will often be different
1497
+ # from the number of input points. Each of the input points should be inside the valid domain (this
1498
+ # can be checked by testing the points' domain flags individually). However, the convex hull of the
1499
+ # input points may go outside the valid domain. The returned convex hull should contain the
1500
+ # transformed image of the intersection of the source convex hull and the source domain.
1501
+ #
1502
+ # @param [Array<<Array<Integer>>] points in tuples of (x,y,z) with z being optional
1503
+ # @return [Array<<Array<Integer>>]
1504
+ def codomain_convex_hull(points)
1505
+ raise NotImplementedError, "#{__method__} is not implemented in the abstract CoordinateTransform class."
1506
+ end
1507
+
1508
+ # Transforms a coordinate point. The passed parameter point should not be modified.
1509
+ #
1510
+ # @param [Integer] x
1511
+ # @param [Integer] y
1512
+ # @param [Integer] z optional
1513
+ # @return [Array<Integer>] transformed point coordinates in (x,y,z) order
1514
+ def transform_coords(x, y, z = nil)
1515
+ raise NotImplementedError, "#{__method__} is not implemented in the abstract CoordinateTransform class."
1516
+ end
1517
+
1518
+ # Transforms a coordinate point. The passed parameter point should not be modified.
1519
+ #
1520
+ # @param [Array<Array<Integer>>] points in (x,y,z) tuples where z is optional
1521
+ # @return [Array<Array<Integer>>] list of transformed point coordinates in (x,y,z) order
1522
+ def transform_list(points)
1523
+ points.map { |x, y, z| transform_coords(x, y, z) }
1524
+ end
1525
+
1526
+ # Creates the inverse transform of this object. This method may fail if the transform is not one to
1527
+ # one. However, all cartographic projections should succeed.
1528
+ #
1529
+ # @return [CoordinateTransform]
1530
+ def inverse
1531
+ self.class.create(target_cs, source_cs)
1532
+ end
1533
+
1534
+ class << self
1535
+ # Initialize a new CoordinateTransform
1536
+ #
1537
+ # Note this class should not be used directly since it does not
1538
+ # implement any transformation logic. It merely defines
1539
+ # what methods actual implementations must use.
1540
+ #
1541
+ # @param [CoordinateSystem] source_cs
1542
+ # @param [CoordinateSystem] target_cs
1543
+ # @param [Array] optional any params for Info or Base
1544
+ # @return [CoordinateTransform]
1545
+ def create(source_cs, target_cs, *optional)
1546
+ new(source_cs, target_cs, optional)
1547
+ end
1548
+ end
1549
+
1550
+ private
1551
+
1552
+ def wkt_content(standard_brackets)
1553
+ source_cs_wkt = "SOURCECS[#{source_cs.to_wkt(standard_brackets: standard_brackets)}]"
1554
+ target_cs_wkt = "TARGETCS[#{target_cs.to_wkt(standard_brackets: standard_brackets)}]"
1555
+
1556
+ [source_cs_wkt, target_cs_wkt]
1557
+ end
1558
+ end
1355
1559
  end
1356
1560
  end
1357
1561
  end