rgeo-dschee 0.5.4

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 (176) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +29 -0
  3. data/ext/geos_c_impl/coordinates.c +65 -0
  4. data/ext/geos_c_impl/coordinates.h +2 -0
  5. data/ext/geos_c_impl/extconf.rb +43 -0
  6. data/ext/geos_c_impl/factory.c +995 -0
  7. data/ext/geos_c_impl/factory.h +238 -0
  8. data/ext/geos_c_impl/geometry.c +1093 -0
  9. data/ext/geos_c_impl/geometry.h +23 -0
  10. data/ext/geos_c_impl/geometry_collection.c +757 -0
  11. data/ext/geos_c_impl/geometry_collection.h +46 -0
  12. data/ext/geos_c_impl/line_string.c +675 -0
  13. data/ext/geos_c_impl/line_string.h +32 -0
  14. data/ext/geos_c_impl/main.c +40 -0
  15. data/ext/geos_c_impl/point.c +236 -0
  16. data/ext/geos_c_impl/point.h +30 -0
  17. data/ext/geos_c_impl/polygon.c +359 -0
  18. data/ext/geos_c_impl/polygon.h +43 -0
  19. data/ext/geos_c_impl/preface.h +38 -0
  20. data/ext/proj4_c_impl/extconf.rb +62 -0
  21. data/ext/proj4_c_impl/main.c +315 -0
  22. data/lib/rgeo.rb +89 -0
  23. data/lib/rgeo/cartesian.rb +25 -0
  24. data/lib/rgeo/cartesian/analysis.rb +77 -0
  25. data/lib/rgeo/cartesian/bounding_box.rb +398 -0
  26. data/lib/rgeo/cartesian/calculations.rb +113 -0
  27. data/lib/rgeo/cartesian/factory.rb +347 -0
  28. data/lib/rgeo/cartesian/feature_classes.rb +100 -0
  29. data/lib/rgeo/cartesian/feature_methods.rb +88 -0
  30. data/lib/rgeo/cartesian/interface.rb +135 -0
  31. data/lib/rgeo/coord_sys.rb +43 -0
  32. data/lib/rgeo/coord_sys/cs/entities.rb +1315 -0
  33. data/lib/rgeo/coord_sys/cs/factories.rb +148 -0
  34. data/lib/rgeo/coord_sys/cs/wkt_parser.rb +272 -0
  35. data/lib/rgeo/coord_sys/proj4.rb +293 -0
  36. data/lib/rgeo/coord_sys/srs_database/interface.rb +115 -0
  37. data/lib/rgeo/coord_sys/srs_database/proj4_data.rb +140 -0
  38. data/lib/rgeo/coord_sys/srs_database/sr_org.rb +62 -0
  39. data/lib/rgeo/coord_sys/srs_database/url_reader.rb +63 -0
  40. data/lib/rgeo/error.rb +27 -0
  41. data/lib/rgeo/feature.rb +54 -0
  42. data/lib/rgeo/feature/curve.rb +111 -0
  43. data/lib/rgeo/feature/factory.rb +278 -0
  44. data/lib/rgeo/feature/factory_generator.rb +96 -0
  45. data/lib/rgeo/feature/geometry.rb +624 -0
  46. data/lib/rgeo/feature/geometry_collection.rb +95 -0
  47. data/lib/rgeo/feature/line.rb +26 -0
  48. data/lib/rgeo/feature/line_string.rb +60 -0
  49. data/lib/rgeo/feature/linear_ring.rb +26 -0
  50. data/lib/rgeo/feature/mixins.rb +143 -0
  51. data/lib/rgeo/feature/multi_curve.rb +71 -0
  52. data/lib/rgeo/feature/multi_line_string.rb +26 -0
  53. data/lib/rgeo/feature/multi_point.rb +33 -0
  54. data/lib/rgeo/feature/multi_polygon.rb +57 -0
  55. data/lib/rgeo/feature/multi_surface.rb +73 -0
  56. data/lib/rgeo/feature/point.rb +84 -0
  57. data/lib/rgeo/feature/polygon.rb +97 -0
  58. data/lib/rgeo/feature/surface.rb +79 -0
  59. data/lib/rgeo/feature/types.rb +284 -0
  60. data/lib/rgeo/geographic.rb +40 -0
  61. data/lib/rgeo/geographic/factory.rb +450 -0
  62. data/lib/rgeo/geographic/interface.rb +489 -0
  63. data/lib/rgeo/geographic/proj4_projector.rb +58 -0
  64. data/lib/rgeo/geographic/projected_feature_classes.rb +107 -0
  65. data/lib/rgeo/geographic/projected_feature_methods.rb +212 -0
  66. data/lib/rgeo/geographic/projected_window.rb +383 -0
  67. data/lib/rgeo/geographic/simple_mercator_projector.rb +110 -0
  68. data/lib/rgeo/geographic/spherical_feature_classes.rb +100 -0
  69. data/lib/rgeo/geographic/spherical_feature_methods.rb +134 -0
  70. data/lib/rgeo/geographic/spherical_math.rb +188 -0
  71. data/lib/rgeo/geos.rb +89 -0
  72. data/lib/rgeo/geos/capi_factory.rb +470 -0
  73. data/lib/rgeo/geos/capi_feature_classes.rb +129 -0
  74. data/lib/rgeo/geos/ffi_factory.rb +592 -0
  75. data/lib/rgeo/geos/ffi_feature_classes.rb +83 -0
  76. data/lib/rgeo/geos/ffi_feature_methods.rb +574 -0
  77. data/lib/rgeo/geos/interface.rb +202 -0
  78. data/lib/rgeo/geos/utils.rb +74 -0
  79. data/lib/rgeo/geos/zm_factory.rb +405 -0
  80. data/lib/rgeo/geos/zm_feature_classes.rb +80 -0
  81. data/lib/rgeo/geos/zm_feature_methods.rb +344 -0
  82. data/lib/rgeo/impl_helper.rb +19 -0
  83. data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +185 -0
  84. data/lib/rgeo/impl_helper/basic_geometry_methods.rb +61 -0
  85. data/lib/rgeo/impl_helper/basic_line_string_methods.rb +146 -0
  86. data/lib/rgeo/impl_helper/basic_point_methods.rb +104 -0
  87. data/lib/rgeo/impl_helper/basic_polygon_methods.rb +87 -0
  88. data/lib/rgeo/impl_helper/math.rb +14 -0
  89. data/lib/rgeo/impl_helper/utils.rb +29 -0
  90. data/lib/rgeo/version.rb +3 -0
  91. data/lib/rgeo/wkrep.rb +37 -0
  92. data/lib/rgeo/wkrep/wkb_generator.rb +201 -0
  93. data/lib/rgeo/wkrep/wkb_parser.rb +251 -0
  94. data/lib/rgeo/wkrep/wkt_generator.rb +207 -0
  95. data/lib/rgeo/wkrep/wkt_parser.rb +415 -0
  96. data/lib/rgeo/yaml.rb +23 -0
  97. data/test/cartesian_analysis_test.rb +65 -0
  98. data/test/cartesian_bbox_test.rb +123 -0
  99. data/test/common/factory_tests.rb +78 -0
  100. data/test/common/geometry_collection_tests.rb +237 -0
  101. data/test/common/line_string_tests.rb +330 -0
  102. data/test/common/multi_line_string_tests.rb +182 -0
  103. data/test/common/multi_point_tests.rb +200 -0
  104. data/test/common/multi_polygon_tests.rb +191 -0
  105. data/test/common/point_tests.rb +370 -0
  106. data/test/common/polygon_tests.rb +261 -0
  107. data/test/coord_sys/ogc_cs_test.rb +342 -0
  108. data/test/coord_sys/proj4_srs_data_test.rb +41 -0
  109. data/test/coord_sys/proj4_test.rb +150 -0
  110. data/test/coord_sys/sr_org_test.rb +32 -0
  111. data/test/coord_sys/url_reader_test.rb +42 -0
  112. data/test/geos_capi/factory_test.rb +31 -0
  113. data/test/geos_capi/geometry_collection_test.rb +24 -0
  114. data/test/geos_capi/line_string_test.rb +24 -0
  115. data/test/geos_capi/misc_test.rb +116 -0
  116. data/test/geos_capi/multi_line_string_test.rb +24 -0
  117. data/test/geos_capi/multi_point_test.rb +24 -0
  118. data/test/geos_capi/multi_polygon_test.rb +39 -0
  119. data/test/geos_capi/parsing_unparsing_test.rb +40 -0
  120. data/test/geos_capi/point_test.rb +72 -0
  121. data/test/geos_capi/polygon_test.rb +154 -0
  122. data/test/geos_capi/zmfactory_test.rb +57 -0
  123. data/test/geos_ffi/factory_test.rb +31 -0
  124. data/test/geos_ffi/geometry_collection_test.rb +24 -0
  125. data/test/geos_ffi/line_string_test.rb +24 -0
  126. data/test/geos_ffi/misc_test.rb +63 -0
  127. data/test/geos_ffi/multi_line_string_test.rb +24 -0
  128. data/test/geos_ffi/multi_point_test.rb +24 -0
  129. data/test/geos_ffi/multi_polygon_test.rb +33 -0
  130. data/test/geos_ffi/parsing_unparsing_test.rb +41 -0
  131. data/test/geos_ffi/point_test.rb +77 -0
  132. data/test/geos_ffi/polygon_test.rb +46 -0
  133. data/test/geos_ffi/zmfactory_test.rb +58 -0
  134. data/test/mixins_test.rb +141 -0
  135. data/test/oneoff_test.rb +26 -0
  136. data/test/projected_geographic/factory_test.rb +25 -0
  137. data/test/projected_geographic/geometry_collection_test.rb +24 -0
  138. data/test/projected_geographic/line_string_test.rb +24 -0
  139. data/test/projected_geographic/multi_line_string_test.rb +26 -0
  140. data/test/projected_geographic/multi_point_test.rb +30 -0
  141. data/test/projected_geographic/multi_polygon_test.rb +25 -0
  142. data/test/projected_geographic/point_test.rb +51 -0
  143. data/test/projected_geographic/polygon_test.rb +24 -0
  144. data/test/simple_cartesian/calculations_test.rb +99 -0
  145. data/test/simple_cartesian/factory_test.rb +27 -0
  146. data/test/simple_cartesian/geometry_collection_test.rb +30 -0
  147. data/test/simple_cartesian/line_string_test.rb +31 -0
  148. data/test/simple_cartesian/multi_line_string_test.rb +28 -0
  149. data/test/simple_cartesian/multi_point_test.rb +31 -0
  150. data/test/simple_cartesian/multi_polygon_test.rb +31 -0
  151. data/test/simple_cartesian/point_test.rb +50 -0
  152. data/test/simple_cartesian/polygon_test.rb +28 -0
  153. data/test/simple_mercator/factory_test.rb +25 -0
  154. data/test/simple_mercator/geometry_collection_test.rb +24 -0
  155. data/test/simple_mercator/line_string_test.rb +24 -0
  156. data/test/simple_mercator/multi_line_string_test.rb +26 -0
  157. data/test/simple_mercator/multi_point_test.rb +29 -0
  158. data/test/simple_mercator/multi_polygon_test.rb +25 -0
  159. data/test/simple_mercator/point_test.rb +55 -0
  160. data/test/simple_mercator/polygon_test.rb +24 -0
  161. data/test/simple_mercator/window_test.rb +173 -0
  162. data/test/spherical_geographic/calculations_test.rb +167 -0
  163. data/test/spherical_geographic/factory_test.rb +27 -0
  164. data/test/spherical_geographic/geometry_collection_test.rb +31 -0
  165. data/test/spherical_geographic/line_string_test.rb +31 -0
  166. data/test/spherical_geographic/multi_line_string_test.rb +29 -0
  167. data/test/spherical_geographic/multi_point_test.rb +31 -0
  168. data/test/spherical_geographic/multi_polygon_test.rb +31 -0
  169. data/test/spherical_geographic/point_test.rb +78 -0
  170. data/test/spherical_geographic/polygon_test.rb +28 -0
  171. data/test/types_test.rb +42 -0
  172. data/test/wkrep/wkb_generator_test.rb +185 -0
  173. data/test/wkrep/wkb_parser_test.rb +293 -0
  174. data/test/wkrep/wkt_generator_test.rb +294 -0
  175. data/test/wkrep/wkt_parser_test.rb +412 -0
  176. metadata +386 -0
@@ -0,0 +1,148 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # OGC CS factory for RGeo
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+
7
+ module RGeo
8
+ module CoordSys
9
+ # This module contains an implementation of the CS (coordinate
10
+ # systems) package of the OGC Coordinate Transform spec. It provides
11
+ # classes for representing ellipsoids, datums, coordinate systems,
12
+ # and other related concepts, as well as a parser for the WKT format
13
+ # for specifying coordinate systems.
14
+ #
15
+ # Generally, the easiest way to create coordinate system objects is
16
+ # to use RGeo::CoordSys::CS.create_from_wkt, which parses the WKT
17
+ # format. You can also use the create methods available for each
18
+ # object class.
19
+ #
20
+ # Most but not all of the spec is implemented here.
21
+ # Currently missing are:
22
+ #
23
+ # * XML format is not implemented. We're assuming that WKT is the
24
+ # preferred format.
25
+ # * The PT and CT packages are not implemented.
26
+ # * FittedCoordinateSystem is not implemented.
27
+ # * The defaultEnvelope attribute of CS_CoordinateSystem is not
28
+ # implemented.
29
+
30
+ module CS
31
+ # A class implementing the CS_CoordinateSystemFactory interface.
32
+ # It provides methods for building up complex objects from simpler
33
+ # objects or values.
34
+ #
35
+ # Note that the methods of CS_CoordinateSystemFactory do not provide
36
+ # facilities for setting the authority. If you need to set authority
37
+ # values, use the create methods for the object classes themselves.
38
+
39
+ class CoordinateSystemFactory
40
+ # Create a CompoundCoordinateSystem from a name, and two
41
+ # constituent coordinate systems.
42
+
43
+ def create_compound_coordinate_system(name_, head_, tail_)
44
+ CompoundCoordinateSystem.create(name_, head_, tail_)
45
+ end
46
+
47
+ # Create an Ellipsoid from a name, semi-major axis, and semi-minor
48
+ # axis. You can also provide a LinearUnit, but this is optional
49
+ # and may be set to nil.
50
+
51
+ def create_ellipsoid(name_, semi_major_axis_, semi_minor_axis_, linear_unit_)
52
+ Ellipsoid.create_ellipsoid(name_, semi_major_axis_, semi_minor_axis_, linear_unit_)
53
+ end
54
+
55
+ # Create an Ellipsoid from a name, semi-major axis, and an inverse
56
+ # flattening factor. You can also provide a LinearUnit, but this
57
+ # is optional and may be set to nil.
58
+
59
+ def create_flattened_sphere(name_, semi_major_axis_, inverse_flattening_, linear_unit_)
60
+ Ellipsoid.create_flattened_sphere(name_, semi_major_axis_, inverse_flattening_, linear_unit_)
61
+ end
62
+
63
+ # Create any object given the OGC WKT format. Raises
64
+ # Error::ParseError if a syntax error is encounterred.
65
+
66
+ def create_from_wkt(str_)
67
+ WKTParser.new(str_).parse
68
+ end
69
+
70
+ # Create a GeographicCoordinateSystem, given a name, an
71
+ # AngularUnit, a HorizontalDatum, a PrimeMeridian, and two
72
+ # AxisInfo objects. The AxisInfo objects are optional and may be
73
+ # set to nil.
74
+
75
+ def create_geographic_coordinate_system(name_, angular_unit_, horizontal_datum_, prime_meridian_, axis0_, axis1_)
76
+ GeographicCoordinateSystem.create(name_, angular_unit_, horizontal_datum_, prime_meridian_, axis0_, axis1_)
77
+ end
78
+
79
+ # Create a HorizontalDatum given a name, a horizontal datum type
80
+ # code, an Ellipsoid, and a WGS84ConversionInfo. The
81
+ # WGS84ConversionInfo is optional and may be set to nil.
82
+
83
+ def create_horizontal_datum(name_, horizontal_datum_type_, ellipsoid_, to_wgs84_)
84
+ HorizontalDatum.create(name_, horizontal_datum_type_, ellipsoid_, to_wgs84_)
85
+ end
86
+
87
+ # Create a LocalCoordinateSystem given a name, a LocalDatum, a
88
+ # Unit, and an array of at least one AxisInfo.
89
+
90
+ def create_local_coordinate_system(name_, datum_, unit_, axes_)
91
+ LocalCoordinateSystem.create(name_, datum_, unit_, axes_)
92
+ end
93
+
94
+ # Create a LocalDatum given a name and a local datum type code.
95
+
96
+ def create_local_datum(_name_, local_datum_type_)
97
+ LocalDatum.create(name, local_datum_type_)
98
+ end
99
+
100
+ # Create a PrimeMeridian given a name, an AngularUnit, and a
101
+ # longitude offset.
102
+
103
+ def create_prime_meridian(_name_, angular_unit_, longitude_)
104
+ PrimeMeridian.create(name, angular_unit_, longitude_)
105
+ end
106
+
107
+ # Create a ProjectedCoordinateSystem given a name, a
108
+ # GeographicCoordinateSystem, and Projection, a LinearUnit, and
109
+ # two AxisInfo objects. The AxisInfo objects are optional and may
110
+ # be set to nil.
111
+
112
+ def create_projected_coordinate_system(name_, gcs_, projection_, linear_unit_, axis0_, axis1_)
113
+ ProjectedCoordinateSystem.create(name_, gcs_, projection_, linear_unit_, axis0_, axis1_)
114
+ end
115
+
116
+ # Create a Projection given a name, a projection class, and an
117
+ # array of ProjectionParameter.
118
+
119
+ def create_projection(name_, wkt_projection_class_, parameters_)
120
+ Projection.create(name_, wkt_projection_class_, parameters_)
121
+ end
122
+
123
+ # Create a VerticalCoordinateSystem given a name, a VerticalDatum,
124
+ # a VerticalUnit, and an AxisInfo. The AxisInfo is optional and
125
+ # may be nil.
126
+
127
+ def create_vertical_coordinate_system(name_, vertical_datum_, vertical_unit_, axis_)
128
+ VerticalCoordinateSystem.create(name_, vertical_datum_, vertical_unit_, axis_)
129
+ end
130
+
131
+ # Create a VerticalDatum given a name ane a datum type code.
132
+
133
+ def create_vertical_datum(name_, vertical_datum_type_)
134
+ VerticalDatum.create(name_, vertical_datum_type_)
135
+ end
136
+ end
137
+
138
+ class << self
139
+ # Parsees OGC WKT format and returns the object created. Raises
140
+ # Error::ParseError if a syntax error is encounterred.
141
+
142
+ def create_from_wkt(str_)
143
+ WKTParser.new(str_).parse
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,272 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # OGC CS wkt parser for RGeo
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+
7
+ module RGeo
8
+ module CoordSys
9
+ module CS
10
+ class WKTParser # :nodoc:
11
+ def initialize(str_)
12
+ @scanner = ::StringScanner.new(str_)
13
+ next_token
14
+ end
15
+
16
+ def parse(containing_type_ = nil) # :nodoc:
17
+ if @cur_token.is_a?(QuotedString) ||
18
+ @cur_token.is_a?(::Numeric) ||
19
+ (containing_type_ == "AXIS" && @cur_token.is_a?(TypeString))
20
+ value_ = @cur_token
21
+ next_token
22
+ return value_
23
+ end
24
+ unless @cur_token.is_a?(TypeString)
25
+ raise Error::ParseError("Found token #{@cur_token} when we expected a value")
26
+ end
27
+ type_ = @cur_token
28
+ next_token
29
+ consume_token_type(:begin)
30
+ args_ = ArgumentList.new
31
+ args_ << parse(type_)
32
+ loop do
33
+ break unless @cur_token == :comma
34
+ next_token
35
+ args_ << parse(type_)
36
+ end
37
+ consume_token_type(:end)
38
+ obj_ = nil
39
+ case type_
40
+ when "AUTHORITY"
41
+ obj_ = AuthorityClause.new(args_.shift(QuotedString), args_.shift(QuotedString))
42
+ when "EXTENSION"
43
+ obj_ = ExtensionClause.new(args_.shift(QuotedString), args_.shift(QuotedString))
44
+ when "AXIS"
45
+ obj_ = AxisInfo.create(args_.shift(QuotedString), args_.shift(TypeString))
46
+ when "TOWGS84"
47
+ bursa_wolf_params_ = args_.find_all(::Numeric)
48
+ unless bursa_wolf_params_.size == 7
49
+ raise Error::ParseError("Expected 7 Bursa Wolf parameters but found #{bursa_wolf_params_.size}")
50
+ end
51
+ obj_ = WGS84ConversionInfo.create(*bursa_wolf_params_)
52
+ when "UNIT"
53
+ case containing_type_
54
+ when "GEOCCS", "VERT_CS", "PROJCS", "SPHEROID"
55
+ klass_ = LinearUnit
56
+ when "GEOGCS"
57
+ klass_ = AngularUnit
58
+ else
59
+ klass_ = Unit
60
+ end
61
+ obj_ = klass_.create(args_.shift(QuotedString), args_.shift(::Numeric), *args_.create_optionals)
62
+ when "PARAMETER"
63
+ obj_ = ProjectionParameter.create(args_.shift(QuotedString), args_.shift(::Numeric))
64
+ when "PRIMEM"
65
+ obj_ = PrimeMeridian.create(args_.shift(QuotedString), nil, args_.shift(::Numeric), *args_.create_optionals)
66
+ when "SPHEROID"
67
+ obj_ = Ellipsoid.create_flattened_sphere(args_.shift(QuotedString), args_.shift(::Numeric), args_.shift(::Numeric), args_.find_first(LinearUnit), *args_.create_optionals)
68
+ when "PROJECTION"
69
+ name_ = args_.shift(QuotedString)
70
+ obj_ = Projection.create(name_, name_, args_.find_all(ProjectionParameter), *args_.create_optionals)
71
+ when "DATUM"
72
+ name_ = args_.shift(QuotedString)
73
+ ellipsoid_ = args_.find_first(Ellipsoid)
74
+ to_wgs84_ = args_.find_first(WGS84ConversionInfo)
75
+ obj_ = HorizontalDatum.create(name_, HD_GEOCENTRIC, ellipsoid_, to_wgs84_, *args_.create_optionals)
76
+ when "VERT_DATUM"
77
+ obj_ = VerticalDatum.create(args_.shift(QuotedString), args_.shift(::Numeric), *args_.create_optionals)
78
+ when "LOCAL_DATUM"
79
+ obj_ = LocalDatum.create(args_.shift(QuotedString), args_.shift(::Numeric), *args_.create_optionals)
80
+ when "COMPD_CS"
81
+ obj_ = CompoundCoordinateSystem.create(args_.shift(QuotedString), args_.shift(CoordinateSystem), args_.shift(CoordinateSystem), *args_.create_optionals)
82
+ when "LOCAL_CS"
83
+ name_ = args_.shift(QuotedString)
84
+ local_datum_ = args_.find_first(LocalDatum)
85
+ unit_ = args_.find_first(Unit)
86
+ axes_ = args_.find_all(AxisInfo)
87
+ unless axes_.size > 0
88
+ raise Error::ParseError("Expected at least one AXIS in a LOCAL_CS")
89
+ end
90
+ obj_ = LocalCoordinateSystem.create(name_, local_datum_, unit_, axes_, *args_.create_optionals)
91
+ when "GEOCCS"
92
+ name_ = args_.shift(QuotedString)
93
+ horizontal_datum_ = args_.find_first(HorizontalDatum)
94
+ prime_meridian_ = args_.find_first(PrimeMeridian)
95
+ linear_unit_ = args_.find_first(LinearUnit)
96
+ axes_ = args_.find_all(AxisInfo)
97
+ unless axes_.size == 0 || axes_.size == 3
98
+ raise Error::ParseError("GEOCCS must contain either 0 or 3 AXIS parameters")
99
+ end
100
+ obj_ = GeocentricCoordinateSystem.create(name_, horizontal_datum_, prime_meridian_, linear_unit_, axes_[0], axes_[1], axes_[2], *args_.create_optionals)
101
+ when "VERT_CS"
102
+ name_ = args_.shift(QuotedString)
103
+ vertical_datum_ = args_.find_first(VerticalDatum)
104
+ linear_unit_ = args_.find_first(LinearUnit)
105
+ axis_ = args_.find_first(AxisInfo)
106
+ obj_ = VerticalCoordinateSystem.create(name_, vertical_datum_, linear_unit_, axis_, *args_.create_optionals)
107
+ when "GEOGCS"
108
+ name_ = args_.shift(QuotedString)
109
+ horizontal_datum_ = args_.find_first(HorizontalDatum)
110
+ prime_meridian_ = args_.find_first(PrimeMeridian)
111
+ angular_unit_ = args_.find_first(AngularUnit)
112
+ axes_ = args_.find_all(AxisInfo)
113
+ unless axes_.size == 0 || axes_.size == 2
114
+ raise Error::ParseError("GEOGCS must contain either 0 or 2 AXIS parameters")
115
+ end
116
+ obj_ = GeographicCoordinateSystem.create(name_, angular_unit_, horizontal_datum_, prime_meridian_, axes_[0], axes_[1], *args_.create_optionals)
117
+ when "PROJCS"
118
+ name_ = args_.shift(QuotedString)
119
+ geographic_coordinate_system_ = args_.find_first(GeographicCoordinateSystem)
120
+ projection_ = args_.find_first(Projection)
121
+ parameters_ = args_.find_all(ProjectionParameter)
122
+ projection_.instance_variable_get(:@parameters).concat(parameters_)
123
+ linear_unit_ = args_.find_first(LinearUnit)
124
+ axes_ = args_.find_all(AxisInfo)
125
+ unless axes_.size == 0 || axes_.size == 2
126
+ raise Error::ParseError("PROJCS must contain either 0 or 2 AXIS parameters")
127
+ end
128
+ obj_ = ProjectedCoordinateSystem.create(name_, geographic_coordinate_system_, projection_, linear_unit_, axes_[0], axes_[1], *args_.create_optionals)
129
+ else
130
+ raise Error::ParseError, "Unrecognized type: #{type_}"
131
+ end
132
+ args_.assert_empty
133
+ obj_
134
+ end
135
+
136
+ def consume_token_type(type_) # :nodoc:
137
+ expect_token_type(type_)
138
+ tok_ = @cur_token
139
+ next_token
140
+ tok_
141
+ end
142
+
143
+ def expect_token_type(type_) # :nodoc:
144
+ unless type_ === @cur_token
145
+ raise Error::ParseError, "#{type_.inspect} expected but #{@cur_token.inspect} found."
146
+ end
147
+ end
148
+
149
+ def next_token # :nodoc:
150
+ @scanner.skip(/\s+/)
151
+ case @scanner.peek(1)
152
+ when '"'
153
+ @scanner.getch
154
+ @cur_token = QuotedString.new(@scanner.scan(/[^"]*/))
155
+ @scanner.getch
156
+ when ","
157
+ @scanner.getch
158
+ @cur_token = :comma
159
+ when "(", "["
160
+ @scanner.getch
161
+ @cur_token = :begin
162
+ when "]", ")"
163
+ @scanner.getch
164
+ @cur_token = :end
165
+ when /[a-zA-Z]/
166
+ @cur_token = TypeString.new(@scanner.scan(/[a-zA-Z]\w*/))
167
+ when "", nil
168
+ @cur_token = nil
169
+ else
170
+ @scanner.scan_until(/[^\s\(\)\[\],"]+/)
171
+ token_ = @scanner.matched
172
+ if token_ =~ /^[-+]?(\d+(\.\d*)?|\.\d+)(e[-+]?\d+)?$/
173
+ @cur_token = token_.to_f
174
+ else
175
+ raise Error::ParseError, "Bad token: #{token_.inspect}"
176
+ end
177
+ end
178
+ @cur_token
179
+ end
180
+
181
+ attr_reader :cur_token
182
+
183
+ class QuotedString < ::String # :nodoc:
184
+ end
185
+
186
+ class TypeString < ::String # :nodoc:
187
+ end
188
+
189
+ class AuthorityClause # :nodoc:
190
+ def initialize(name_, code_) # :nodoc:
191
+ @name = name_
192
+ @code = code_
193
+ end
194
+
195
+ def to_a # :nodoc:
196
+ [@name, @code]
197
+ end
198
+ end
199
+
200
+ class ExtensionClause # :nodoc:
201
+ def initialize(key_, value_) # :nodoc:
202
+ @key = key_
203
+ @value = value_
204
+ end
205
+
206
+ attr_reader :key # :nodoc:
207
+ attr_reader :value # :nodoc:
208
+ end
209
+
210
+ class ArgumentList # :nodoc:
211
+ def initialize # :nodoc:
212
+ @values = []
213
+ end
214
+
215
+ def <<(value_) # :nodoc:
216
+ @values << value_
217
+ end
218
+
219
+ def assert_empty # :nodoc:
220
+ if @values.size > 0
221
+ names_ = @values.map do |val_|
222
+ val_.is_a?(Base) ? val_._wkt_typename : val_.inspect
223
+ end
224
+ raise Error::ParseError, "#{@values.size} unexpected arguments: #{names_.join(', ')}"
225
+ end
226
+ end
227
+
228
+ def find_first(klass_) # :nodoc:
229
+ @values.each_with_index do |val_, index_|
230
+ if val_.is_a?(klass_)
231
+ @values.slice!(index_)
232
+ return val_
233
+ end
234
+ end
235
+ nil
236
+ end
237
+
238
+ def find_all(klass_) # :nodoc:
239
+ results_ = []
240
+ nvalues_ = []
241
+ @values.each do |val_|
242
+ if val_.is_a?(klass_)
243
+ results_ << val_
244
+ else
245
+ nvalues_ << val_
246
+ end
247
+ end
248
+ @values = nvalues_
249
+ results_
250
+ end
251
+
252
+ def create_optionals # :nodoc:
253
+ hash_ = {}
254
+ find_all(ExtensionClause).each { |ec_| hash_[ec_.key] = ec_.value }
255
+ (find_first(AuthorityClause) || [nil, nil]).to_a + [nil, nil, nil, hash_]
256
+ end
257
+
258
+ def shift(klass_ = nil) # :nodoc:
259
+ val_ = @values.shift
260
+ unless val_
261
+ raise Error::ParseError, "No arguments left... expected #{klass_}"
262
+ end
263
+ if klass_ && !val_.is_a?(klass_)
264
+ raise Error::ParseError, "Expected #{klass_} but got #{val_.class}"
265
+ end
266
+ val_
267
+ end
268
+ end
269
+ end
270
+ end
271
+ end
272
+ end
@@ -0,0 +1,293 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Proj4 wrapper for RGeo
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+
7
+ module RGeo
8
+ module CoordSys
9
+ # This is a Ruby wrapper around a Proj4 coordinate system.
10
+ # It represents a single geographic coordinate system, which may be
11
+ # a flat projection, a geocentric (3-dimensional) coordinate system,
12
+ # or a geographic (latitude-longitude) coordinate system.
13
+ #
14
+ # Generally, these are used to define the projection for a
15
+ # Feature::Factory. You can then convert between coordinate systems
16
+ # by casting geometries between such factories using the :project
17
+ # option. You may also use this object directly to perform low-level
18
+ # coordinate transformations.
19
+
20
+ class Proj4
21
+ def inspect # :nodoc:
22
+ "#<#{self.class}:0x#{object_id.to_s(16)} #{canonical_str.inspect}>"
23
+ end
24
+
25
+ def to_s # :nodoc:
26
+ canonical_str
27
+ end
28
+
29
+ def hash # :nodoc:
30
+ @hash ||= canonical_hash.hash
31
+ end
32
+
33
+ # Returns true if this Proj4 is equivalent to the given Proj4.
34
+ #
35
+ # Note: this tests for equivalence by comparing only the hash
36
+ # definitions of the Proj4 objects, and returning true if those
37
+ # definitions are equivalent. In some cases, this may still return
38
+ # false even if the actual coordinate systems are identical, since
39
+ # there are sometimes multiple ways to express a given coordinate
40
+ # system.
41
+
42
+ def eql?(rhs_)
43
+ rhs_.class == self.class && rhs_.canonical_hash == canonical_hash && rhs_._radians? == _radians?
44
+ end
45
+ alias_method :==, :eql?
46
+
47
+ # Marshal support
48
+
49
+ def marshal_dump # :nodoc:
50
+ { "rad" => radians?, "str" => original_str || canonical_str }
51
+ end
52
+
53
+ def marshal_load(data_) # :nodoc:
54
+ _set_value(data_["str"], data_["rad"])
55
+ end
56
+
57
+ # Psych support
58
+
59
+ def encode_with(coder_) # :nodoc:
60
+ coder_["proj4"] = original_str || canonical_str
61
+ coder_["radians"] = radians?
62
+ end
63
+
64
+ def init_with(coder_) # :nodoc:
65
+ if coder_.type == :scalar
66
+ _set_value(coder_.scalar, false)
67
+ else
68
+ _set_value(coder_["proj4"], coder_["radians"])
69
+ end
70
+ end
71
+
72
+ # Returns the "canonical" string definition for this coordinate
73
+ # system, as reported by Proj4. This may be slightly different
74
+ # from the definition used to construct this object.
75
+
76
+ def canonical_str
77
+ unless defined?(@canonical_str)
78
+ @canonical_str = _canonical_str
79
+ if @canonical_str.respond_to?(:force_encoding)
80
+ @canonical_str.force_encoding("US-ASCII")
81
+ end
82
+ end
83
+ @canonical_str
84
+ end
85
+
86
+ # Returns the "canonical" hash definition for this coordinate
87
+ # system, as reported by Proj4. This may be slightly different
88
+ # from the definition used to construct this object.
89
+
90
+ def canonical_hash
91
+ unless defined?(@canonical_hash)
92
+ @canonical_hash = {}
93
+ canonical_str.strip.split(/\s+/).each do |elem_|
94
+ @canonical_hash[Regexp.last_match(1)] = Regexp.last_match(3) if elem_ =~ /^\+(\w+)(=(\S+))?$/
95
+ end
96
+ end
97
+ @canonical_hash
98
+ end
99
+
100
+ # Returns the string definition originally used to construct this
101
+ # object. Returns nil if this object wasn't created by a string
102
+ # definition; i.e. if it was created using get_geographic.
103
+
104
+ def original_str
105
+ _original_str
106
+ end
107
+
108
+ # Returns true if this Proj4 object is a geographic (lat-long)
109
+ # coordinate system.
110
+
111
+ def geographic?
112
+ _geographic?
113
+ end
114
+
115
+ # Returns true if this Proj4 object is a geocentric (3dz)
116
+ # coordinate system.
117
+
118
+ def geocentric?
119
+ _geocentric?
120
+ end
121
+
122
+ # Returns true if this Proj4 object uses radians rather than degrees
123
+ # if it is a geographic coordinate system.
124
+
125
+ def radians?
126
+ _radians?
127
+ end
128
+
129
+ # Get the geographic (unprojected lat-long) coordinate system
130
+ # corresponding to this coordinate system; i.e. the one that uses
131
+ # the same ellipsoid and datum.
132
+
133
+ def get_geographic
134
+ _get_geographic
135
+ end
136
+
137
+ class << self
138
+ # Returns true if Proj4 is supported in this installation.
139
+ # If this returns false, the other methods such as create
140
+ # will not work.
141
+
142
+ def supported?
143
+ respond_to?(:_create)
144
+ end
145
+
146
+ # Returns the Proj library version as a string of the format "x.y.z".
147
+
148
+ def version
149
+ ::RGeo::VERSION
150
+ end
151
+
152
+ # Create a new Proj4 object, given a definition, which may be
153
+ # either a string or a hash. Returns nil if the given definition
154
+ # is invalid or Proj4 is not supported.
155
+ #
156
+ # Recognized options include:
157
+ #
158
+ # [<tt>:radians</tt>]
159
+ # If set to true, then this proj4 will represent geographic
160
+ # (latitude/longitude) coordinates in radians rather than
161
+ # degrees. If this is a geographic coordinate system, then its
162
+ # units will be in radians. If this is a projected coordinate
163
+ # system, then its units will be unchanged, but any geographic
164
+ # coordinate system obtained using get_geographic will use
165
+ # radians as its units. If this is a geocentric or other type of
166
+ # coordinate system, this has no effect. Default is false.
167
+ # (That is all coordinates are in degrees by default.)
168
+
169
+ def create(defn_, opts_ = {})
170
+ result_ = nil
171
+ if supported?
172
+ if defn_.is_a?(::Hash)
173
+ defn_ = defn_.map { |k_, v_| v_ ? "+#{k_}=#{v_}" : "+#{k_}" }.join(" ")
174
+ end
175
+ unless defn_ =~ /^\s*\+/
176
+ defn_ = defn_.sub(/^(\s*)/, '\1+').gsub(/(\s+)([^+\s])/, '\1+\2')
177
+ end
178
+ result_ = _create(defn_, opts_[:radians])
179
+ result_ = nil unless result_._valid?
180
+ end
181
+ result_
182
+ end
183
+
184
+ # Create a new Proj4 object, given a definition, which may be
185
+ # either a string or a hash. Raises Error::UnsupportedOperation
186
+ # if the given definition is invalid or Proj4 is not supported.
187
+ #
188
+ # Recognized options include:
189
+ #
190
+ # [<tt>:radians</tt>]
191
+ # If set to true, then this proj4 will represent geographic
192
+ # (latitude/longitude) coordinates in radians rather than
193
+ # degrees. If this is a geographic coordinate system, then its
194
+ # units will be in radians. If this is a projected coordinate
195
+ # system, then its units will be unchanged, but any geographic
196
+ # coordinate system obtained using get_geographic will use
197
+ # radians as its units. If this is a geocentric or other type of
198
+ # coordinate system, this has no effect. Default is false.
199
+ # (That is all coordinates are in degrees by default.)
200
+
201
+ def new(defn_, opts_ = {})
202
+ result_ = create(defn_, opts_)
203
+ unless result_
204
+ raise Error::UnsupportedOperation, "Proj4 not supported in this installation"
205
+ end
206
+ result_
207
+ end
208
+
209
+ # Low-level coordinate transform method.
210
+ # Transforms the given coordinate (x, y, [z]) from one proj4
211
+ # coordinate system to another. Returns an array with either two
212
+ # or three elements.
213
+
214
+ def transform_coords(from_proj_, to_proj_, x_, y_, z_ = nil)
215
+ if !from_proj_._radians? && from_proj_._geographic?
216
+ x_ *= ImplHelper::Math::RADIANS_PER_DEGREE
217
+ y_ *= ImplHelper::Math::RADIANS_PER_DEGREE
218
+ end
219
+ result_ = _transform_coords(from_proj_, to_proj_, x_, y_, z_)
220
+ if result_ && !to_proj_._radians? && to_proj_._geographic?
221
+ result_[0] *= ImplHelper::Math::DEGREES_PER_RADIAN
222
+ result_[1] *= ImplHelper::Math::DEGREES_PER_RADIAN
223
+ end
224
+ result_
225
+ end
226
+
227
+ # Low-level geometry transform method.
228
+ # Transforms the given geometry between the given two projections.
229
+ # The resulting geometry is constructed using the to_factory.
230
+ # Any projections associated with the factories themselves are
231
+ # ignored.
232
+
233
+ def transform(from_proj_, from_geometry_, to_proj_, to_factory_)
234
+ case from_geometry_
235
+ when Feature::Point
236
+ _transform_point(from_proj_, from_geometry_, to_proj_, to_factory_)
237
+ when Feature::Line
238
+ to_factory_.line(from_geometry_.points.map { |p_| _transform_point(from_proj_, p_, to_proj_, to_factory_) })
239
+ when Feature::LinearRing
240
+ _transform_linear_ring(from_proj_, from_geometry_, to_proj_, to_factory_)
241
+ when Feature::LineString
242
+ to_factory_.line_string(from_geometry_.points.map { |p_| _transform_point(from_proj_, p_, to_proj_, to_factory_) })
243
+ when Feature::Polygon
244
+ _transform_polygon(from_proj_, from_geometry_, to_proj_, to_factory_)
245
+ when Feature::MultiPoint
246
+ to_factory_.multi_point(from_geometry_.map { |p_| _transform_point(from_proj_, p_, to_proj_, to_factory_) })
247
+ when Feature::MultiLineString
248
+ to_factory_.multi_line_string(from_geometry_.map { |g_| transform(from_proj_, g_, to_proj_, to_factory_) })
249
+ when Feature::MultiPolygon
250
+ to_factory_.multi_polygon(from_geometry_.map { |p_| _transform_polygon(from_proj_, p_, to_proj_, to_factory_) })
251
+ when Feature::GeometryCollection
252
+ to_factory_.collection(from_geometry_.map { |g_| transform(from_proj_, g_, to_proj_, to_factory_) })
253
+ end
254
+ end
255
+
256
+ def _transform_point(from_proj_, from_point_, to_proj_, to_factory_) # :nodoc:
257
+ from_factory_ = from_point_.factory
258
+ from_has_z_ = from_factory_.property(:has_z_coordinate)
259
+ from_has_m_ = from_factory_.property(:has_m_coordinate)
260
+ to_has_z_ = to_factory_.property(:has_z_coordinate)
261
+ to_has_m_ = to_factory_.property(:has_m_coordinate)
262
+ x_ = from_point_.x
263
+ y_ = from_point_.y
264
+ if !from_proj_._radians? && from_proj_._geographic?
265
+ x_ *= ImplHelper::Math::RADIANS_PER_DEGREE
266
+ y_ *= ImplHelper::Math::RADIANS_PER_DEGREE
267
+ end
268
+ coords_ = _transform_coords(from_proj_, to_proj_, x_, y_, from_has_z_ ? from_point_.z : nil)
269
+ if coords_
270
+ if !to_proj_._radians? && to_proj_._geographic?
271
+ coords_[0] *= ImplHelper::Math::DEGREES_PER_RADIAN
272
+ coords_[1] *= ImplHelper::Math::DEGREES_PER_RADIAN
273
+ end
274
+ extras_ = []
275
+ extras_ << coords_[2].to_f if to_has_z_
276
+ extras_ << from_has_m_ ? from_point_.m : 0.0 if to_has_m_
277
+ to_factory_.point(coords_[0], coords_[1], *extras_)
278
+ end
279
+ end
280
+
281
+ def _transform_linear_ring(from_proj_, from_ring_, to_proj_, to_factory_) # :nodoc:
282
+ to_factory_.linear_ring(from_ring_.points[0..-2].map { |p_| _transform_point(from_proj_, p_, to_proj_, to_factory_) })
283
+ end
284
+
285
+ def _transform_polygon(from_proj_, from_polygon_, to_proj_, to_factory_) # :nodoc:
286
+ ext_ = _transform_linear_ring(from_proj_, from_polygon_.exterior_ring, to_proj_, to_factory_)
287
+ int_ = from_polygon_.interior_rings.map { |r_| _transform_linear_ring(from_proj_, r_, to_proj_, to_factory_) }
288
+ to_factory_.polygon(ext_, int_)
289
+ end
290
+ end
291
+ end
292
+ end
293
+ end