rgeo 2.3.1 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
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