schleyfox-rgeo 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. data/History.rdoc +199 -0
  2. data/README.rdoc +172 -0
  3. data/Spatial_Programming_With_RGeo.rdoc +440 -0
  4. data/Version +1 -0
  5. data/ext/geos_c_impl/extconf.rb +84 -0
  6. data/ext/geos_c_impl/factory.c +468 -0
  7. data/ext/geos_c_impl/factory.h +224 -0
  8. data/ext/geos_c_impl/geometry.c +705 -0
  9. data/ext/geos_c_impl/geometry.h +55 -0
  10. data/ext/geos_c_impl/geometry_collection.c +482 -0
  11. data/ext/geos_c_impl/geometry_collection.h +69 -0
  12. data/ext/geos_c_impl/line_string.c +509 -0
  13. data/ext/geos_c_impl/line_string.h +64 -0
  14. data/ext/geos_c_impl/main.c +70 -0
  15. data/ext/geos_c_impl/point.c +193 -0
  16. data/ext/geos_c_impl/point.h +62 -0
  17. data/ext/geos_c_impl/polygon.c +265 -0
  18. data/ext/geos_c_impl/polygon.h +66 -0
  19. data/ext/geos_c_impl/preface.h +50 -0
  20. data/ext/proj4_c_impl/extconf.rb +88 -0
  21. data/ext/proj4_c_impl/main.c +271 -0
  22. data/lib/rgeo.rb +124 -0
  23. data/lib/rgeo/cartesian.rb +60 -0
  24. data/lib/rgeo/cartesian/analysis.rb +118 -0
  25. data/lib/rgeo/cartesian/bounding_box.rb +337 -0
  26. data/lib/rgeo/cartesian/calculations.rb +161 -0
  27. data/lib/rgeo/cartesian/factory.rb +209 -0
  28. data/lib/rgeo/cartesian/feature_classes.rb +173 -0
  29. data/lib/rgeo/cartesian/feature_methods.rb +106 -0
  30. data/lib/rgeo/cartesian/interface.rb +150 -0
  31. data/lib/rgeo/coord_sys.rb +79 -0
  32. data/lib/rgeo/coord_sys/cs/entities.rb +1524 -0
  33. data/lib/rgeo/coord_sys/cs/factories.rb +208 -0
  34. data/lib/rgeo/coord_sys/cs/wkt_parser.rb +308 -0
  35. data/lib/rgeo/coord_sys/proj4.rb +312 -0
  36. data/lib/rgeo/coord_sys/srs_database/active_record_table.rb +194 -0
  37. data/lib/rgeo/coord_sys/srs_database/interface.rb +165 -0
  38. data/lib/rgeo/coord_sys/srs_database/proj4_data.rb +188 -0
  39. data/lib/rgeo/coord_sys/srs_database/sr_org.rb +108 -0
  40. data/lib/rgeo/coord_sys/srs_database/url_reader.rb +108 -0
  41. data/lib/rgeo/error.rb +63 -0
  42. data/lib/rgeo/feature.rb +88 -0
  43. data/lib/rgeo/feature/curve.rb +156 -0
  44. data/lib/rgeo/feature/factory.rb +332 -0
  45. data/lib/rgeo/feature/factory_generator.rb +138 -0
  46. data/lib/rgeo/feature/geometry.rb +614 -0
  47. data/lib/rgeo/feature/geometry_collection.rb +129 -0
  48. data/lib/rgeo/feature/line.rb +66 -0
  49. data/lib/rgeo/feature/line_string.rb +102 -0
  50. data/lib/rgeo/feature/linear_ring.rb +66 -0
  51. data/lib/rgeo/feature/multi_curve.rb +113 -0
  52. data/lib/rgeo/feature/multi_line_string.rb +66 -0
  53. data/lib/rgeo/feature/multi_point.rb +73 -0
  54. data/lib/rgeo/feature/multi_polygon.rb +97 -0
  55. data/lib/rgeo/feature/multi_surface.rb +116 -0
  56. data/lib/rgeo/feature/point.rb +120 -0
  57. data/lib/rgeo/feature/polygon.rb +141 -0
  58. data/lib/rgeo/feature/surface.rb +122 -0
  59. data/lib/rgeo/feature/types.rb +305 -0
  60. data/lib/rgeo/geographic.rb +75 -0
  61. data/lib/rgeo/geographic/factory.rb +287 -0
  62. data/lib/rgeo/geographic/interface.rb +410 -0
  63. data/lib/rgeo/geographic/proj4_projector.rb +98 -0
  64. data/lib/rgeo/geographic/projected_feature_classes.rb +213 -0
  65. data/lib/rgeo/geographic/projected_feature_methods.rb +228 -0
  66. data/lib/rgeo/geographic/projected_window.rb +467 -0
  67. data/lib/rgeo/geographic/simple_mercator_projector.rb +157 -0
  68. data/lib/rgeo/geographic/spherical_feature_classes.rb +212 -0
  69. data/lib/rgeo/geographic/spherical_feature_methods.rb +97 -0
  70. data/lib/rgeo/geographic/spherical_math.rb +206 -0
  71. data/lib/rgeo/geos.rb +72 -0
  72. data/lib/rgeo/geos/factory.rb +301 -0
  73. data/lib/rgeo/geos/impl_additions.rb +76 -0
  74. data/lib/rgeo/geos/interface.rb +139 -0
  75. data/lib/rgeo/geos/zm_factory.rb +275 -0
  76. data/lib/rgeo/geos/zm_impl.rb +432 -0
  77. data/lib/rgeo/impl_helper.rb +53 -0
  78. data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +235 -0
  79. data/lib/rgeo/impl_helper/basic_geometry_methods.rb +85 -0
  80. data/lib/rgeo/impl_helper/basic_line_string_methods.rb +197 -0
  81. data/lib/rgeo/impl_helper/basic_point_methods.rb +138 -0
  82. data/lib/rgeo/impl_helper/basic_polygon_methods.rb +121 -0
  83. data/lib/rgeo/impl_helper/math.rb +50 -0
  84. data/lib/rgeo/version.rb +52 -0
  85. data/lib/rgeo/wkrep.rb +72 -0
  86. data/lib/rgeo/wkrep/wkb_generator.rb +267 -0
  87. data/lib/rgeo/wkrep/wkb_parser.rb +315 -0
  88. data/lib/rgeo/wkrep/wkt_generator.rb +275 -0
  89. data/lib/rgeo/wkrep/wkt_parser.rb +496 -0
  90. data/test/common/geometry_collection_tests.rb +238 -0
  91. data/test/common/line_string_tests.rb +324 -0
  92. data/test/common/multi_line_string_tests.rb +209 -0
  93. data/test/common/multi_point_tests.rb +201 -0
  94. data/test/common/multi_polygon_tests.rb +208 -0
  95. data/test/common/point_tests.rb +331 -0
  96. data/test/common/polygon_tests.rb +232 -0
  97. data/test/coord_sys/tc_active_record_table.rb +102 -0
  98. data/test/coord_sys/tc_ogc_cs.rb +356 -0
  99. data/test/coord_sys/tc_proj4.rb +138 -0
  100. data/test/coord_sys/tc_proj4_srs_data.rb +76 -0
  101. data/test/coord_sys/tc_sr_org.rb +70 -0
  102. data/test/coord_sys/tc_url_reader.rb +82 -0
  103. data/test/geos/tc_factory.rb +91 -0
  104. data/test/geos/tc_geometry_collection.rb +62 -0
  105. data/test/geos/tc_line_string.rb +62 -0
  106. data/test/geos/tc_misc.rb +72 -0
  107. data/test/geos/tc_multi_line_string.rb +62 -0
  108. data/test/geos/tc_multi_point.rb +62 -0
  109. data/test/geos/tc_multi_polygon.rb +63 -0
  110. data/test/geos/tc_point.rb +86 -0
  111. data/test/geos/tc_polygon.rb +86 -0
  112. data/test/geos/tc_zmfactory.rb +85 -0
  113. data/test/projected_geographic/tc_geometry_collection.rb +62 -0
  114. data/test/projected_geographic/tc_line_string.rb +62 -0
  115. data/test/projected_geographic/tc_multi_line_string.rb +62 -0
  116. data/test/projected_geographic/tc_multi_point.rb +62 -0
  117. data/test/projected_geographic/tc_multi_polygon.rb +63 -0
  118. data/test/projected_geographic/tc_point.rb +93 -0
  119. data/test/projected_geographic/tc_polygon.rb +62 -0
  120. data/test/simple_cartesian/tc_calculations.rb +145 -0
  121. data/test/simple_cartesian/tc_geometry_collection.rb +69 -0
  122. data/test/simple_cartesian/tc_line_string.rb +70 -0
  123. data/test/simple_cartesian/tc_multi_line_string.rb +67 -0
  124. data/test/simple_cartesian/tc_multi_point.rb +67 -0
  125. data/test/simple_cartesian/tc_multi_polygon.rb +70 -0
  126. data/test/simple_cartesian/tc_point.rb +91 -0
  127. data/test/simple_cartesian/tc_polygon.rb +67 -0
  128. data/test/simple_mercator/tc_geometry_collection.rb +62 -0
  129. data/test/simple_mercator/tc_line_string.rb +62 -0
  130. data/test/simple_mercator/tc_multi_line_string.rb +62 -0
  131. data/test/simple_mercator/tc_multi_point.rb +62 -0
  132. data/test/simple_mercator/tc_multi_polygon.rb +63 -0
  133. data/test/simple_mercator/tc_point.rb +93 -0
  134. data/test/simple_mercator/tc_polygon.rb +62 -0
  135. data/test/simple_mercator/tc_window.rb +219 -0
  136. data/test/spherical_geographic/tc_calculations.rb +203 -0
  137. data/test/spherical_geographic/tc_geometry_collection.rb +70 -0
  138. data/test/spherical_geographic/tc_line_string.rb +70 -0
  139. data/test/spherical_geographic/tc_multi_line_string.rb +67 -0
  140. data/test/spherical_geographic/tc_multi_point.rb +67 -0
  141. data/test/spherical_geographic/tc_multi_polygon.rb +70 -0
  142. data/test/spherical_geographic/tc_point.rb +100 -0
  143. data/test/spherical_geographic/tc_polygon.rb +67 -0
  144. data/test/tc_cartesian_analysis.rb +107 -0
  145. data/test/tc_oneoff.rb +63 -0
  146. data/test/wkrep/tc_wkb_generator.rb +249 -0
  147. data/test/wkrep/tc_wkb_parser.rb +353 -0
  148. data/test/wkrep/tc_wkt_generator.rb +362 -0
  149. data/test/wkrep/tc_wkt_parser.rb +480 -0
  150. metadata +267 -0
@@ -0,0 +1,199 @@
1
+ === 0.2.5 / 2011-03-21
2
+
3
+ * Line segment intersection tests in the simple cartesian implementations were failing for a few cases involving collinear segments. Fixed. (Reported by Dimitry Solovyov.)
4
+ * Argument hash RDocs should be more readable.
5
+
6
+ === 0.2.4 / 2010-12-31
7
+
8
+ * Several bugs were preventing the low-level Proj4 transform functions from working at all. Fixed. (Reported by mRg.)
9
+ * The GEOS factories over-optimized projection casts, sometimes resulting in proj4 transformations not getting applied. Fixed. (Reported by mRg.)
10
+ * Proj4 objects now have a flag to indicate whether geographic coordinate systems should be in radians. The (undocumented) radians option is no longer supported in transform_coords.
11
+ * Disabled the spatialreference.org tests for the time being because the site seems to be offline.
12
+
13
+ === 0.2.3 / 2010-12-19
14
+
15
+ * The "simpler mercator" geographic type incorrectly reported EPSG 3857 instead of EPSG 3785 for the projection. Dyslexia fixed.
16
+ * Geographic types couldn't have their coord_sys set. Fixed.
17
+ * You can now pass an :srs_database option when creating most factory types. This lets the factory look up its coordinate system using the given SRID.
18
+ * There are now explicit methods you can call to obtain FactoryGenerator objects; you should not need to call <tt>method</tt>.
19
+ * Wrote RDocs for all the CoordSys::CS and CoordSys::SRSDatabase classes.
20
+
21
+ === 0.2.2 / 2010-12-15
22
+
23
+ The main theme for this release was support for spatial reference system databases. The basic functionality is done and ready for experimentation. However, documentation is still in progress, and we're still working on some ideas to make coordinate system management more seamless by integrating the SRS databases with FactoryGenerator.
24
+
25
+ * Implemented OGC coordinate system objects, including most of the CS package of the OGC Coordinate Transform spec, and a parser for the WKT.
26
+ * Defined interfaces for spatial reference system database access. Implemented a database based on ActiveRecord and backed by spatial_ref_sys tables; one based on the data files shared by the proj4 library; one based on retrieving data from spatialreference.org, and one based on retrieving data from arbitrary URLs.
27
+ * Renamed RGeo::Feature::Type::Instance marker module to RGeo::Feature::Instance. The old name is aliased for backward compatibility but is deprecated.
28
+ * Added a few more directories to the default lookup path for geos and proj4.
29
+
30
+ === 0.2.1 / 2010-12-09
31
+
32
+ * Now compatible with Rubinius (version 1.1 or later).
33
+ * Now partially compatible with JRuby (1.5 or later). A bunch of tests fail because GEOS and Proj4 are not available, hence there is no projection support and no complete Cartesian implementation. But at least RGeo loads and the basic operations work.
34
+ * Some minor optimizations in the GEOS glue code.
35
+
36
+ === 0.2.0 / 2010-12-07
37
+
38
+ This is the first public alpha version of RGeo. With this version, we are soft-locking the API interfaces and will try to retain backwards compatibility from this point. Incompatible API changes may still be done, but only if considered necessary.
39
+
40
+ With this release, RGeo has been split into a core library and a set of optional modules. The following modules remain in the core "rgeo" gem:
41
+ * RGeo::Feature
42
+ * RGeo::CoordSys
43
+ * RGeo::Geos
44
+ * RGeo::Cartesian
45
+ * RGeo::Geographic
46
+ * RGeo::WKRep
47
+
48
+ The following modules have been spun off into separate gems:
49
+ * RGeo::GeoJSON has been spun off into the "rgeo-geojson" gem.
50
+ * RGeo::Shapefile has been spun off into the "rgeo-shapefile" gem.
51
+ * RGeo::ActiveRecord has been spun off into the "rgeo-activerecord" gem.
52
+
53
+ The ActiveRecord adapters have been spun off into gems according to the recommended ActiveRecord naming scheme:
54
+ * The <b>mysqlspatial</b> adapter is now in the gem "activerecord-mysqlspatial-adapter".
55
+ * The <b>mysql2spatial</b> adapter is now in the gem "activerecord-mysql2spatial-adapter".
56
+ * The <b>spatialite</b> adapter is now in the gem "activerecord-spatialite-adapter".
57
+ * The <b>postgis</b> adapter is now in the gem "activerecord-postgis-adapter".
58
+
59
+ Any additional modules likely will be distributed similarly as separate gems.
60
+
61
+ Other changes in this version:
62
+ * API CHANGE: Renamed UnsupportedCapability to UnsupportedOperation since we've done away with the "capability" concept.
63
+ * Proj4 integration wasn't building into the right location on a gem install. Fixed.
64
+ * Various updates to the rdocs.
65
+ * Minor updates to the Spatial Programming paper.
66
+
67
+ === 0.1.22 / 2010-12-05
68
+
69
+ This should be the last pre-alpha development version. The next version planned is the 0.2 alpha release.
70
+
71
+ * API CHANGE: Renamed Geography module to Geographic.
72
+ * API CHANGE: Renamed Factory#has_capability? to Factory#property to generalize the API.
73
+ * API CHANGE: Factory#proj4 and Factory#coord_sys are now required methods.
74
+ * The ZM Geos factory didn't properly handle proj4. Fixed.
75
+ * The proj4-based projected geographic factory now extracts the cooresponding geographic coordinate system from the projection, rather than always using WGS84.
76
+ * Initial draft of Spatial Programming paper.
77
+
78
+ === 0.1.21 / 2010-12-03
79
+
80
+ * API CHANGE: Added "_factory" to the end of the Geography toplevel interface methods, for consistency with the rest of the API.
81
+ * API CHANGE: Simplified initializer API for WKTParser and WKBParser.
82
+ * API CHANGE: Removed ActiveRecord::Base.rgeo_default_factory, and provided a reasonable default rgeo_factory_generator.
83
+ * Removed deprecated pluralized names RGeo::Features and RGeo::Errors.
84
+ * First pass implementation of the ActiveRecord adapters for SpatiaLite and PostGIS.
85
+ * Fixed problems with Proj4 equivalence testing.
86
+ * Several more minor fixes and documentation updates.
87
+
88
+ === 0.1.20 / 2010-11-30
89
+
90
+ * API CHANGE: Methods that raised MethodUnimplemented now raise UnsupportedCapability instead. Removed MethodUnimplemented.
91
+ * API CHANGE: Renamed RGeo::Features to RGeo::Feature, RGeo::Errors to RGeo::Error, and RGeo::ImplHelpers to RGeo::ImplHelper. The old pluralized names are aliased to the new names for now for backward compatibility, though they are deprecated and will be removed shortly.
92
+ * Renamed the tests directory to test. Generally speaking, I'm getting rid of pluralized names.
93
+ * Added RGeo::CoordSys::Proj4 representing a proj4 coordinate system. It uses the proj4 library.
94
+ * Added Factory#proj4 as an optional method indicated by the :proj4 capability.
95
+ * All existing geometry implementations now support proj4.
96
+ * You can now cause casting to transform between proj4 projections by specifying the :project parameter.
97
+ * A Geography implementation with an arbitrary projection backed by proj4 is now available.
98
+
99
+ === 0.1.19 / 2010-11-23
100
+
101
+ * The GEOS implementation now supports ZM (4-dimensional data), via a wrapper since the underlying GEOS library doesn't support 4d data natively.
102
+ * Added a BoundingBox tool to the Cartesian module.
103
+ * Fleshed out a few more methods of SimpleCartesian and SimpleSpherical.
104
+ * The simple Cartesian point implementation included a bit more leakage from the Geography implementations (pole equivalence, lat/lon methods). Fixed.
105
+ * Under certain circumstances, collections and polygons using GEOS could lose their Z or M coordinates. Fixed.
106
+ * There were some cases when implementations based on ImplHelpers were pulling in the wrong methods. Fixed.
107
+ * Taking the envelope of an empty GEOS collection yielded an illegal object that could cause a crash. Fixed. It now yields an empty collection.
108
+ * Taking the boundary of an empty GEOS collection yielded nil. Fixed. It now yields an empty collection.
109
+
110
+ === 0.1.18 / 2010-11-22
111
+
112
+ * API CHANGE: GeoJSON defaults to no JSON parser rather than to the JSON library. GeoJSON also fails better when attempting to use a JSON parser that isn't installed.
113
+ * Added a decorator tool for FactoryGenerator
114
+ * Added an analysis module for Cartesian geometries, and implemented an algorithm for determining whether a ring is clockwise or counterclockwise. (We needed this to interpret shapefiles.)
115
+ * First pass implementation of shapefile reading. It passes a basic test suite I borrowed from shapelib, but I haven't yet done an exhaustive test on every case.
116
+ * The simple Cartesian implementation mistakenly clamped x and y to lat/lon limits. Fixed.
117
+
118
+ === 0.1.17 / 2010-11-20
119
+
120
+ * Implemented ActiveRecord adapters that cover MySQL Spatial for the mysql and mysql2 gems. SpatiaLite and PostGIS adapters are coming later.
121
+ * Added and documented FactoryGenerator.
122
+ * API CHANGE: WKRep parsers now take FactoryGenerator rather than the ad-hoc factory_from_srid.
123
+ * API CHANGE: Factory#override_cast now takes its optional flags in a hash so it can be extended more cleanly in the future.
124
+
125
+ === 0.1.16 / 2010-11-18
126
+
127
+ * Test coverage for WKB generator and parser; fixed a few bugs.
128
+ * Eliminated the hard dependency on the JSON gem, and allow configuration of GeoJSON with an alternate JSON parser such as YAJL or ActiveSupport.
129
+ * API CHANGE: geo factory is now a hash option in GeoJSON, and is now optional.
130
+ * GeoJSON now handles Z and M coordinates, according to the capabilities of the geo factory.
131
+ * GeoJSON feature objects can now handle null geometry and null properties, per the spec.
132
+ * Factory::cast now optionally lets you pass the parameters as a hash.
133
+
134
+ === 0.1.15 / 2010-11-08
135
+
136
+ * Cleanup, fixes, documentation, and partial test coverage in the WKT/WKB implementations.
137
+ * Implemented autoload for the various modules.
138
+ * A few minor fixes.
139
+
140
+ === 0.1.14 / 2010-11-04
141
+
142
+ * Introduced capability checking API.
143
+ * Standardized API and semantics for handling of points with Z and/or M coordinates.
144
+ * Fixed several problems with Z coordinates in GEOS.
145
+ * Fixed exceptions and wrong values returned from GEOS LineString#start_point and LineString#end_point.
146
+ * Fixed crash in GEOS LineString#point_n when the index was out of bounds.
147
+ * Fixed GEOS line string closed test.
148
+ * Implemented support for Z and M coordinates in GEOS, SimpleCartesian, SimpleMercator, and SimpleSpherical. GEOS and SimpleMercator support Z or M but not both at once, because the underlying GEOS library supports only 3 dimensions. SimpleCartesian and SimpleSpherical can support both at once.
149
+ * Implemented parsers and generators for WKT/WKB and EWKT/EWKB in Ruby, providing full support for all generally used cases including 3 and 4 dimensional data and embedded SRIDs. This implementation is used by default by all feature implementations except GEOS, which continues to use its own internal WKT/WKB implementation unless the Ruby implementation is invoked explicitly.
150
+
151
+ === 0.1.13 / 2010-10-26
152
+
153
+ * Reworked the way casting is done. Casting has two dimensions: factory casting and type casting, either or both of which can be done at once. Implemented a standard casting algorithm to handle these cases, and an override mechanism for factories that want to do some of their own casting. Removed Factory#cast and Geometry#cast, and implemented a global Features::cast entry point instead.
154
+ * All factory and relational methods now perform auto-casting on inputs.
155
+ * Removed the "auto-flattening" behavior of Factory#multi_point, Factory#multi_line_string, and Factory#multi_polygon because it seemed overkill for factory methods. These methods now just attempt to auto-cast the immediate objects.
156
+ * Filled out more test cases for SimpleSpherical.
157
+ * Provided SimpleCartesian as a fallback implementation in case Geos is not available. SimpleCartesian is like SimpleSpherical in that some operations are not provided, but it is pure ruby and doesn't depend on external libraries.
158
+ * Improved feature type checking facilities.
159
+ * Documentation updates.
160
+
161
+ === 0.1.12 / 2010-10-23
162
+
163
+ * API CHANGE: Factory#coerce renamed to Factory#cast. I think this should be the final name for this function.
164
+ * Some new tests and a lot of fixes in SimpleMercator and SimpleSpherical.
165
+ * Implemented a few more pieces of SimpleSpherical. Notably, LineString#is_simple? (which should now allow LinearRing to work).
166
+ * Classes that included Features::Geometry had their === operator erroneously overridden. Fixed.
167
+ * A few more documentation updates.
168
+
169
+ === 0.1.11 / 2010-10-21
170
+
171
+ * API CHANGE: Factory#convert renamed to Factory#coerce.
172
+ * Some implementations that inherit from RGeo::Features::Geometry (e.g. the Geography implementations) raised Unimplemented from operator implementations because they had aliased the wrong methods. Fixed.
173
+ * Geos coercer didn't properly coerce "contained" elements in a compound geometry. Fixed.
174
+ * The SimpleMercator and SimpleSpherical factories failed to properly check and coerce inputs into the typed collection methods. Fixed.
175
+ * The options for the SimpleMercator factory were not documented and didn't work. Fixed.
176
+ * A bunch of additional test cases and minor fixes for SimpleMercator and SimpleSpherical.
177
+
178
+ === 0.1.10 / 2010-10-19
179
+
180
+ Initial public release. This release is considered pre-alpha quality and is being released for experimentation and feedback. We are using it in production in a limited capacity at GeoPage, but we do not yet recommend general production deployment because there are a number of known bugs and incomplete areas, and many features and APIs are still in flux.
181
+
182
+ Status:
183
+
184
+ * GEOS-based Cartesian implementation is tested and should be fairly stable.
185
+ * GeoJSON parsers and generators are tested and should be fairly stable.
186
+ * Parts of SimpleMercator implementation are fairly stable, but test coverage of more advanced features is still lacking.
187
+ * SimpleSpherical implementation is under construction and not yet available.
188
+ * Rails (ActiveRecord or ActiveModel) integration is pending.
189
+ * Several other integration features, including possible SimpleGeo integration, are pending.
190
+
191
+ Changes since 0.1.9:
192
+
193
+ * Eliminated a few (harmless) compiler warnings when compiling the GEOS bridge under Ruby 1.9.2 on Snow Leopard.
194
+ * Modified file headers, copyright notices, and README files for public release.
195
+ * Changed name from "gp_rgeo" to "rgeo" for public release.
196
+
197
+ === 0.1.9
198
+
199
+ This and earlier versions were tested internally at GeoPage, Inc. but not publicly released.
@@ -0,0 +1,172 @@
1
+ == RGeo
2
+
3
+ RGeo is a geospatial data library for Ruby.
4
+
5
+ === Summary
6
+
7
+ RGeo is a key component for writing location-aware applications in the
8
+ Ruby programming language. At its core is an implementation of the
9
+ industry standard OGC Simple Features Specification, which provides data
10
+ representations of geometric objects such as points, lines, and polygons,
11
+ along with a set of geometric analysis operations. This makes it ideal
12
+ for modeling geolocation data. It also supports a suite of optional add-on
13
+ modules that provide various geolocation-related services.
14
+
15
+ Use the core *rgeo* gem to:
16
+
17
+ * Represent spatial and geolocation data objects such as points, lines,
18
+ and polygons in your Ruby application.
19
+ * Perform standard spatial analysis operations such as finding
20
+ intersections, creating buffers, and computing lengths and areas.
21
+ * Correctly handle spherical geometry, and compute geographic projections
22
+ for map display and data analysis.
23
+ * Read and write location data in the WKT and WKB representations used by
24
+ spatial databases.
25
+
26
+ Several optional modules are currently available:
27
+
28
+ * Generate and interpret GeoJSON data for communication with common
29
+ location-based web services such as SimpleGeo, using the
30
+ <b>rgeo-geojson</b> gem.
31
+ * Read GIS datasets from ESRI shapefiles using the <b>rgeo-shapefile</b>
32
+ gem.
33
+ * Extend ActiveRecord to handle spatial data in MySQL Spatial, SpatiaLite,
34
+ and PostGIS using RGeo's spatial ActiveRecord adapters. These are
35
+ available via the gems:
36
+ * <b>activerecord-mysqlspatial-adapter</b>
37
+ * <b>activerecord-mysql2spatial-adapter</b>
38
+ * <b>activerecord-spatialite-adapter</b>
39
+ * <b>activerecord-postgis-adapter</b>
40
+ * and more to come...
41
+
42
+ === Dependencies
43
+
44
+ RGeo is known to work with the following Ruby implementations:
45
+
46
+ * Standard "MRI" Ruby 1.8.7 or later. (1.9.2 or later preferred.)
47
+ * Rubinius 1.1 or later.
48
+ * Partial support for JRuby 1.5 or later, but a bunch of features are
49
+ missing because GEOS and Proj are not available from Java. We plan on
50
+ integrating with JTS or possibly ffi-geos in the future.
51
+
52
+ Some features also require the following:
53
+
54
+ * GEOS 3.2 or later is highly recommended. Some functions will not be
55
+ available without it. This C/C++ library may be available via your
56
+ operating system's package manager, or you can download it from
57
+ http://trac.osgeo.org/geos
58
+ * Proj 4.7 or later is also recommended. This library is needed if you
59
+ want to translate coordinates between geographic projections. It also
60
+ may be available via your operating system's package manager, or from
61
+ http://trac.osgeo.org/proj
62
+
63
+ Note: The build system assumes a unix-like environment. Windows builds
64
+ may be possible, but not likely "out of the box".
65
+
66
+ === Installation
67
+
68
+ Install RGeo as a gem:
69
+
70
+ gem install rgeo
71
+
72
+ Note: By default, the gem installation looks for the GEOS library in the
73
+ following locations:
74
+
75
+ * /usr/local
76
+ * /usr/local/geos
77
+ * /opt/local
78
+ * /opt/geos
79
+ * /opt
80
+ * /usr
81
+
82
+ If GEOS has been installed in a different location, you must provide its
83
+ installation prefix directory using the "--with-geos-dir" option. This
84
+ option must be preceded by "--" to separate it, as a build switch, from
85
+ the switches interpreted by the gem command. For example:
86
+
87
+ gem install rgeo -- --with-geos-dir=/path/to/my/geos/installation
88
+
89
+ Similarly, the gem installation looks for the Proj4 library in the
90
+ following locations by default:
91
+
92
+ * /usr/local
93
+ * /usr/local/proj
94
+ * /usr/local/proj4
95
+ * /opt/local
96
+ * /opt/proj
97
+ * /opt/proj4
98
+ * /opt
99
+ * /usr
100
+
101
+ If Proj4 is installed in a different location, you must provide its
102
+ installation prefix directory using the "--with-proj-dir" option.
103
+
104
+ === To-do list
105
+
106
+ The RGeo suite of tools is evolving rapidly. The current to-do list for
107
+ the core library includes:
108
+
109
+ * Ellipsoidal geography implementation, possibly utilizing geographiclib.
110
+ * JRuby support via the JTS library, or possibly via ffi-geos.
111
+ * Windows build support.
112
+
113
+ Each of the current add-on modules also has its own feature roadmap, and
114
+ we are planning on introducing more add-on modules, including:
115
+
116
+ * GeoRSS and KML format support.
117
+ * Integration with the SimpleGeo API.
118
+ * Possible additional ActiveRecord adapters.
119
+
120
+ === Development and support
121
+
122
+ Documentation is available at http://virtuoso.rubyforge.org/rgeo/README_rdoc.html
123
+
124
+ Source code is hosted on Github at http://github.com/dazuma/rgeo
125
+
126
+ Contributions are welcome. Fork the project on Github.
127
+
128
+ Report bugs on Github issues at http://github.org/dazuma/rgeo/issues
129
+
130
+ Contact the author at dazuma at gmail dot com.
131
+
132
+ === Acknowledgments
133
+
134
+ RGeo is written by Daniel Azuma (http://www.daniel-azuma.com).
135
+
136
+ Development of RGeo is sponsored by GeoPage, Inc. (http://www.geopage.com).
137
+
138
+ RGeo calls the GEOS library to handle most Cartesian geometric
139
+ calculations, and the Proj4 library to handle projections and coordinate
140
+ transformations. These libraries are maintained by the Open Source
141
+ Geospatial Foundation; more information is available on OSGeo's web site
142
+ (http://www.osgeo.org).
143
+
144
+ === License
145
+
146
+ Copyright 2010 Daniel Azuma
147
+
148
+ All rights reserved.
149
+
150
+ Redistribution and use in source and binary forms, with or without
151
+ modification, are permitted provided that the following conditions are met:
152
+
153
+ * Redistributions of source code must retain the above copyright notice,
154
+ this list of conditions and the following disclaimer.
155
+ * Redistributions in binary form must reproduce the above copyright notice,
156
+ this list of conditions and the following disclaimer in the documentation
157
+ and/or other materials provided with the distribution.
158
+ * Neither the name of the copyright holder, nor the names of any other
159
+ contributors to this software, may be used to endorse or promote products
160
+ derived from this software without specific prior written permission.
161
+
162
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
163
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
164
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
165
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
166
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
167
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
168
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
169
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
170
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
171
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
172
+ POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,440 @@
1
+ = An Introduction to Spatial Programming With RGeo
2
+
3
+ * by Daniel Azuma
4
+ * version 0.3 (17 Feb 2011)
5
+
6
+ == Introduction
7
+
8
+ === About This Document
9
+
10
+ One of the most important current trends in the high-tech industry is the rise of spatial and location-based technologies. Once the exclusive domain of complex GIS systems, these technologies are now increasingly available in small applications, websites, and enterprises. This document provides a brief overview of the concepts, techniques, and tools for implementing location-aware application features, focusing on the Ruby programming language and an open-source technology stack.
11
+
12
+ The contents of this document are as follows.
13
+
14
+ * Section 1 is a short introduction to geospatial technology, including a survey of the common tools and libraries available.
15
+ * Section 2 introduces the standard spatial data types such as points, lines, and polygons used by most geospatial applications.
16
+ * Section 3 summarizes the standard spatial operations that are defined on those data types.
17
+ * Section 4 discusses coordinate systems and geographic projections, and why it is important to get them right.
18
+ * Section 5 covers the most common open source spatial databases.
19
+ * Section 6 briefly covers interoperability with location services and other externally-sourced geospatial data.
20
+
21
+ Geographic information systems (GIS) is a broad and highly sophisticated field, and this introduction will only scratch the surface of the current state of the art. The goal is not to be comprehensive, but to summarize the important elements, and reference outside resources for readers seeking more detailed information.
22
+
23
+ === About The Author
24
+
25
+ {Daniel Azuma}[http://www.daniel-azuma.com/] is the chief software architect and a co-founder of {GeoPage, Inc.}[http://www.geopage.com/], a Seattle-based startup developing location-aware consumer applications. Daniel has been working with Ruby on Rails since 2006, and has a background in computer graphics and visualization. He is also the author of \RGeo, the advanced spatial data library for Ruby covered in this document.
26
+
27
+ == 1. Space: The Next Frontier
28
+
29
+ === 1.1. Why Spatial Programming?
30
+
31
+ By 2010, location had established itself as one of the hottest emerging technological trends. In January, a Juniper Research report predicted that mobile location services alone could drive revenues of nearly $13 billion by 2014 (see {TechCrunch article}[http://techcrunch.com/2010/02/23/location-based-services-revenue/]), while location dominated new feature offerings from a myriad of startups as well as from giants such as Facebook and Twitter. Although the underlying disciplines of computer-assisted cartography and geographic information systems (GIS) have been around for several decades, they have broken into mainstream consumer technology only very recently. This has largely been due to a few key developments, notably, the success of online mapping applications beginning with Google Maps, and the ubiquity of mobile GPS devices especially in mobile phones.
32
+
33
+ Despite this growing interest, location-aware applications remain difficult to develop because the concepts and techniques involved are only beginning to make their way into the mainstream developer consciousness and tools. The primary purpose of this document is to cover the basics that a Ruby or Ruby On Rails developer needs to know when developing with location data, and to introduce the tools and resources that are available.
34
+
35
+ === 1.2. The Emerging Spatial Ecosystem
36
+
37
+ Fortunately, a number of software libraries and organizations now exist to promote and assist developing spatial applications. Here we will survey some of the popular and emerging open software systems available for integration into your location-aware application.
38
+
39
+ Visualization tools have advanced considerably in recent years. Mapping services such as {Google Maps}[http://maps.google.com/] and {Bing Maps}[http://www.bing.com/maps/] now have extensive API support for developing mapping applications. An open mapping service, {OpenStreetMap}[http://www.openstreetmap.org/], has also been launched and is gaining momentum. In addition, tools which let you serve your own map data, such as {OpenLayers}[http://openlayers.org/] and {PolyMaps}[http://polymaps.org/], have also appeared.
40
+
41
+ Most major relational databases now support spatial extensions. The {MySQL}[http://mysql.com/] database provides basic spatial column support out of the box. Third-party add-on libraries exist for {Sqlite3}[http://www.sqlite.org/] and {PostgreSQL}[http://www.postgresql.org/] in the form of {SpatiaLite}[http://www.gaia-gis.it/spatialite/] and {PostGIS}[http://www.postgis.org/], respectively. Commercial databases such as Oracle and Microsoft SQL Server also provide facilities for storing and querying spatial data. Spatial features are also appearing in non-relational data stores. {MongoDB}[http://www.mongodb.org/] recently introduced geospatial indexing, {Solr}[http://lucene.apache.org/solr/] is expected to support spatial queries in the next release of its Lucene-based search engine, and {Sphinx}[http://sphinxsearch.com/] also provides limited spatial search capabilities.
42
+
43
+ A variety of data services have also appeared. Geocoding, the process approximating a latitude/longitude coordinate from a street address, is now offered by most major mapping service vendors such as {Google}[http://code.google.com/apis/maps/documentation/geocoding/], {Microsoft}[http://www.microsoft.com/maps/developers/], and {Yahoo}[http://developer.yahoo.com/geo/placefinder/]. Place databases with geocoded business and major location listings are now also available from a variety of vendors. Several services, notably {SimpleGeo}[http://www.simplegeo.com/], have recently appeared for cloud-based storage and querying of custom location data.
44
+
45
+ Integrating these existing services in a web application is often a challenge, but a few integration libraries and frameworks do exist. {GeoDjango}[http://geodjango.org/] is an add-on for the Python-based Django web framework for building location-based applications. {RGeo}[http://github.com/dazuma/rgeo] is a suite of libraries for Ruby that can perform the same function for Ruby on Rails.
46
+
47
+ Perhaps most important of all, however, are the organizations that have appeared to support the development of geospatial standards and software. The {Open Geospatial Consortium}[http://www.opengeospatial.org/] (OGC) is an international consortium of companies, government agencies, and other organizations that promote interoperability by developing open standards and interfaces for geospatial software systems. Many of the concepts, data types, and operations described in this document were standardized by the OGC. The {Open Source Geospatial Foundation}[http://www.osgeo.org/] develops and supports a variety of open source geospatial software, including PostGIS, GEOS, Proj, and others we will cover in this document. The {OGP Geomatics Committee}[http://www.epsg.org/] (formerly EPSG, the European Petroleum Survey Group) is part of an industry association maintaining the <i>de facto</i> standard EPSG geodetic data set, a set of coordinate systems and transformations used internationally to describe global position. These and other organizations form the backbone of geospatial technology, and most geospatial applications will interact at least indirectly with their services.
48
+
49
+ === 1.3. Ruby Libraries and \RGeo
50
+
51
+ Ruby developers have had access to a fair number of spatial tools, primarily integration libraries for external services. {Geokit}[http://geokit.rubyforge.org/] provides a common interface for querying geocoding services, and a basic ActiveRecord extension for simple spatial queries. {YM4R}[http://ym4r.rubyforge.org/] provides a simple interface for integrating the Google and Yahoo map visualization tools in a Ruby application. Finally, {GeoRuby}[http://georuby.rubyforge.org/] provides classes for basic spatial data types such as points, lines, and polygons, and the add-on library {spatial_adapter}[http://github.com/fragility/spatial_adapter] hacks a few of the popular ActiveRecord database adapters to support spatial columns in the database.
52
+
53
+ In this document, we will cover {RGeo}[http://github.com/dazuma/rgeo], a new spatial data library for Ruby that provides a complete and robust implementation of the standard OGC spatial data types and operations. It covers some of the same functionality as GeoRuby and spatial_adapter. However, where GeoRuby implements only a minimal subset of the OGC feature interfaces, \RGeo supports the entire specification, as well as providing many features and extensions not available with the older libraries.
54
+
55
+ \RGeo comprises several libraries, distributed as gems: a core library, and a suite of optional add-on modules. The core library, distributed as the {rgeo}[http://github.com/dazuma/rgeo] gem, includes the spatial data implementation itself. Currently available add-on modules include {rgeo-geojson}[http://github.com/dazuma/rgeo-geojson], which reads and writes the {GeoJSON}[http://www.geojson.org/] format, and {rgeo-shapefile}[http://github.com/dazuma/rgeo-shapefile], which reads ESRI shapefiles. A number of ActiveRecord adapters also utilize \RGeo to communicate with spatial databases; these include {mysqlspatial}[http://github.com/dazuma/activerecord-mysqlspatial-adapter], {mysql2spatial}[http://github.com/dazuma/activerecord-mysql2spatial-adapter], {spatialite}[http://github.com/dazuma/activerecord-spatialite-adapter], and {postgis}[http://github.com/dazuma/activerecord-postgis-adapter].
56
+
57
+ == 2. Spatial Data Types
58
+
59
+ This section will cover the standard types of spatial data used in geospatial applications.
60
+
61
+ === 2.1. The Simple Features Specification
62
+
63
+ The Open Geospatial Consortium (OGC) defines and publishes a specification entitled "{Geographic information -- Simple feature access}[http://www.opengeospatial.org/standards/sfa]", which defines, among other things, a set of spatial data types and operations that can be done on them. This standard, which we will refer to as the Simple Features Specification (SFS), defines the core types and interfaces used by most spatial applications and databases. Although more recent versions of the spec are now available, most current implementations, including \RGeo, follow version 1.1 of the SFS, and this is the specification we will cover here.
64
+
65
+ A "feature" in the SFS is a geometric object in 2 or 3 dimensional space. These objects can be points, lines, curves, surfaces, and polygons-- in general, most 0, 1, or 2 dimensional objects are supported. Each of these objects is identified by coordinates (X, Y, and sometimes Z), and has an object-oriented interface associated with it, defining a set of operations that can be done. In \RGeo, these interfaces exist as modules in the RGeo::Feature namespace.
66
+
67
+ We will quickly cover the types of geometric objects supported, and then discuss how to use \RGeo to create and manipulate spatial data as Ruby objects.
68
+
69
+ === 2.2. Coordinates
70
+
71
+ Geometric objects generally exist in a two-dimensional domain (such as a plane or a globe) identified by X and Y coordinates. These coordinates could be screen coordinates, as on a map displayed on a computer screen, they could be longitude/latitude coordinates, where X represents longitude and Y represents latitude, or they could be in a different coordinate system altogether.
72
+
73
+ Strictly speaking, version 1.1 of the SFS supports only two dimensions. However, many implementations can represent up to four coordinates, including an optional Z and M coordinate. Z, when present, is generally used for a third dimension of location; for example, it could represent altitude, a distance above or below the surface of the earth. M, when present, is used to represent a "measure", a scalar value that could change across an object. The measure could, for example, represent temperature, population density, or some other function of location. Most current implementations, though they can represent and store Z and M, will not actually perform any analysis with those coordinates; they act merely as additional data fields.
74
+
75
+ === 2.3. Point
76
+
77
+ An SFS "Point" is a single 0-dimensional point in space. Points are typically used in location applications to represent a single location, displayed as a map marker. You can retrieve the coordinates X and Y (and optionally Z and M) from a point object.
78
+
79
+ === 2.4. LineString
80
+
81
+ An SFS "LineString" is a set of one or more connected line segments representing a single continuous, piecewise linear path. It could, for example, represent the path of a single street, the full driving directions from one point to another, or the path of a waterway. It is defined by connecting a series of points in order with line segments, and you can retrieve those points from the LineString object.
82
+
83
+ LineString itself has two subclases, Line and LinearRing. Line is restricted to a single line segment (i.e. two points). LinearRing is a "closed" LineString, in which the two endpoints are coincident.
84
+
85
+ (LineString is actually a subclass of the more general abstract class "Curve", which need not be piecewise linear. However, Curve is not by itself instantiable, and the current SFS version does not actually specify another type of instantiable Curve that is not a LineString.)
86
+
87
+ === 2.5. Polygon
88
+
89
+ An SFS "Polygon" is a connected region of two-dimensional space. Its outer boundary is defined as a LinearRing, so it can have any number of straight "sides". Polygons can also optionally contain "holes", represented by inner boundaries which are also LinearRings. You can retrieve the outer and inner boundaries from a Polygon object as LinearRing objects. Polygons are ideal for representing single plots of land such as property boundaries, or larger regions of the earth's surface such as city, state, or national boundaries, time zones, lakes, and so forth, as long as they are contiguous.
90
+
91
+ (Polygon is also a subclass of a more general abstract class, this one called "Surface". In general, the boundaries of a Surface need not be piecewise linear. However, Surface is not by itself instantiable, and the current SFS version does not actually specify another type of instantiable Surface other than Polygon.)
92
+
93
+ === 2.6. MultiPoint
94
+
95
+ An SFS "MultiPoint" is a collection of zero or more Point objects. You might, for example, represent the locations of all your favorite restaurants as a MultiPoint.
96
+
97
+ === 2.7. MultiLineString
98
+
99
+ An SFS "MultiLineString" is a collection of zero or more LineString objects. It might be used, for example, to represent all the currently "congested" sections of a city's freeways during rush hour. It could even, in principle, represent the entire street map of a city, though such a data structure might be too large to be practical.
100
+
101
+ (MultiLineString is a subclass of the non-instantiable abstract class MultiCurve.)
102
+
103
+ === 2.8. MultiPolygon
104
+
105
+ An SFS "MultiPolygon" is a collection of zero or more Polygon objects. It also has a few additional restrictions, notably that the constituent polygons must all be disjoint and cannot overlap. MultiPolygons are used to represent a region which could have multiple unconnected parts.
106
+
107
+ (MultiPolygon is a subclass of the non-instantiable abstract class MultiSurface.)
108
+
109
+ === 2.9. Geometry and GeometryCollection
110
+
111
+ The various geometric data types described above are arranged in a class hierarchy. The base class of this hierarchy is Geometry. The MultiPoint, MultiLineString, and MultiPolygon types (or more precisely, MultiPoint, MultiCurve, and MultiSurface) are subclasses of a more general data type called GeometryCollection. GeometryCollection can also be instantiated by itself. It represents a general collection of other geometry objects, each of which can be any type.
112
+
113
+ For complete details on the geometry class hierarchy, download the actual {Simple Features Specification}[http://www.opengeospatial.org/standards/sfa]. I recommend downloading version 1.1, because the newer versions (1.2 and later) describe additional types and features that are not commonly available in current implementations. That additional information may be confusing.
114
+
115
+ === 2.10. \RGeo Geometry Factories
116
+
117
+ The data types we have covered here are actually merely interface specifications. \RGeo provides several different concrete implementations of these data type interfaces, intended for different use cases. For example, the RGeo::Geos implementation is \RGeo's main implementation which provides every data type and every operation defined by the SFS. However, that implementation requires a third-party C library, GEOS, to be installed. In cases where that library is not available, \RGeo provides an alternative, the Simple Cartesian implementation, which is a pure Ruby implementation that provides every data type but does not implement some of the more advanced geometric operations. \RGeo also provides further implementations that are designed specifically to work with geographic (longitude/latitude) coordinates, and different projections and ways of performing calculations on the earth's surface. These different implementations are described in more detail in the section on Coordinate Systems and Projections.
118
+
119
+ Each concrete implementation is represented in \RGeo by a factory. A factory is an object that represents the coordinate system and other settings for that implementation. It also provides methods for creating actual geometric objects for that implementation, as defined in the RGeo::Feature::Factory interface. For example:
120
+
121
+ factory = get_my_factory()
122
+ point1 = factory.point(1, 2)
123
+ point2 = factory.point(3, -1)
124
+ line = factory.line_string([point1, point2])
125
+
126
+ The most common factory used by \RGeo is the "preferred Cartesian" factory. This factory uses a flat (Cartesian) coordinate system, and is implemented by GEOS (if available) or using the pure Ruby alternative if not. It can be retrieved by calling:
127
+
128
+ factory = ::RGeo::Cartesian.preferred_factory()
129
+
130
+ Another common factory you might want to use is the "simple Mercator" factory. This is a geographic factory intended for simple location-based applications that use Google or Bing Maps as a visualization tool. Its coordinate system is longitude-latitude, but it also has a built-in facility for converting those coordinates to the flat "tiling" coordinate system used by the map. You can retrieve it by calling:
131
+
132
+ factory = ::RGeo::Geographic.simple_mercator_factory()
133
+
134
+ In many cases, these factory creation methods take additional optional arguments that enable various features. For example, the preferred Cartesian factory, by default, uses only X and Y coordinates. You can activate Z and/or M coordinates by passing an appropriate argument, e.g.:
135
+
136
+ factory = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true)
137
+ factory.property(:has_z_coordinate) # returns true
138
+ factory.property(:has_m_coordinate) # returns false
139
+ point = factory.point(1, 2, 3) # this point has a Z coordinate
140
+
141
+ Note that, in many cases, the factory class itself as well as the actual implementation classes for the geometric objects, are opaque in \RGeo. You should refer to the appropriate interfaces in the RGeo::Feature namespace for the methods you can call.
142
+
143
+ == 3. Spatial Operations
144
+
145
+ In addition to representing geometric data, the SFS interfaces define a suite of basic operations on this data. These operations are available in many forms, depending on the type of software system. Spatial databases such as PostGIS define these as SQL functions that can be used to write queries. \RGeo's goal is to make geometric objects available to Ruby programs, and so these operations are exposed as methods on the geometric Ruby objects.
146
+
147
+ These operations cover a wide range of functionality, and some involve difficult problems of computational geometry, especially over a non-flat coordinate system such as geographic coordinates. \RGeo provides a complete implementation for flat Cartesian coordinates that utilizes the {GEOS}[http://trac.osgeo.org/geos/] library internally. However, some of \RGeo's other implementations provide only a subset of these operations. If you use the PostGIS database, you will find a similar situation. The "geometry" data types actually use GEOS internally to perform geometric computations, and pretty much all functions are available. However, the "geography" data type, which operates on a curved coordinate system, implements only a handful of the defined functions.
148
+
149
+ === 3.1. Basic Properties
150
+
151
+ Most geometry types have a "degenerate" form representing no geometry. For example, a GeometryCollection may contain no items, or a LineString may contain no points. This state is indicated by the <tt>Geometry#is_empty?</tt> method. In \RGeo, any geometry type except Point may be empty.
152
+
153
+ factory = ::RGeo::Cartesian.preferred_factory
154
+ factory.point(1, 2).is_empty? # returns false
155
+ factory.collection([]).is_empty? # returns true
156
+
157
+ A second common property of geometry objects is "simplicity", which basically means the geometry doesn't intersect or repeat itself. For example, a LineString that intersects itself is not simple, nor is a MultiPoint that contains the same point more than once. Sometimes, a geometric analysis algorithm will have simplicity as a precondition. This property is indicated by the <tt>Geometry#is_simple?</tt> method.
158
+
159
+ factory = ::RGeo::Cartesian.preferred_factory
160
+ p00 = factory.point(0, 0)
161
+ p01 = factory.point(0, 1)
162
+ p11 = factory.point(1, 1)
163
+ p10 = factory.point(1, 0)
164
+ zigzag_line = factory.line_string([p00, p10, p01, p11])
165
+ zigzag_line.is_simple? # returns true
166
+ self_crossing_line = factory.line_string([p00, p11, p01, p10])
167
+ self_crossing_line.is_simple? # returns false
168
+
169
+ All geometry objects also contain a "spatial reference ID", returned by the <tt>Geometry#srid</tt> method. This is an external ID reference indicating the "spatial reference system", or coordinate system in use by the geometry. See the section on Coordinate Systems and Projections for further discussion.
170
+
171
+ === 3.2. Relational Operations
172
+
173
+ The SFS specifies a suite of comparison operations that test geometric objects for such relational predicates as equality, overlap, containment, and so forth. In \RGeo, these operations are implemented by methods that return booleans. e.g.
174
+
175
+ if polygon1.overlaps?(polygon2)
176
+ # do something
177
+ end
178
+
179
+ I do not have space here to describe the different comparison operations in detail. See the SFS for the precise defintions. However, I do want to point out one particular feature of \RGeo related to equality checking. The Ruby language has a number of different methods for testing different "forms" of equality. For example:
180
+
181
+ 1 == 1 # => true
182
+ 1 == 1.0 # => true (because the values are the same)
183
+ 1.eql?(1) # => true
184
+ 1.eql?(1.0) # => false (because the classes are different)
185
+ a = "foo"
186
+ b = "foo"
187
+ a == b # => true
188
+ a.eql?(b) # => true
189
+ a.equals?(b) # => false (because they are different objects)
190
+ a.equals?(a) # => true
191
+
192
+ In general, Ruby has three forms of equality: value equality (tested by the <tt>==</tt> operator), object equality (tested by the <tt>eql?</tt> method), and object identity (tested by the <tt>equals?</tt> method).
193
+
194
+ Similarly, \RGeo's equality checking comes in several forms: geometric equality, object equality, and object identity. Geometric equality is tested by the SFS method <tt>equal?</tt>, as well as the <tt>==</tt> operator. This type of equality indicates two objects that may be different representations of the same geometry, for example, a LineString and its reverse, or a Point and a MultiPoint that contains only that same point. Object equality, tested by <tt>eql?</tt>, means the same representation but possibly distinct objects. Object identity, tested by <tt>equals?</tt>, represents the same object, as with other Ruby types.
195
+
196
+ p1 = factory.point(1, 1)
197
+ p2 = factory.point(1, 1)
198
+ mp = factory.multi_point([p1])
199
+ p1 == p2 # => true
200
+ p1.equal?(mp) # => true
201
+ p1 == mp # => true
202
+ p1.eql?(mp) # => false
203
+ p1.eql?(p2) # => true
204
+ p1.equals?(p2) # => false
205
+ p1.equals?(p1) # => true
206
+
207
+ === 3.3. Binary Spatial Operations
208
+
209
+ The SFS also provides several operations that take two geometries and yield a third. For example, you can calculate the intersection, union, or difference between two geometries. In addition to the methods specified by the SFS interfaces, \RGeo provides operators for some of these calculations.
210
+
211
+ p1 = factory.point(1, 1)
212
+ p2 = factory.point(2, 2)
213
+ union = p1.union(p2) # or p1 + p2
214
+ union.geometry_type # returns RGeo::Feature::MultiPoint
215
+ union.num_geometries # returns 2
216
+ diff = p1.difference(p2) # or p1 - p2
217
+ diff.is_empty? # returns true
218
+
219
+ === 3.4. Unary Spatial Operations
220
+
221
+ Methods are provided to compute the boundary of an object, the envelope (i.e. the bounding box), and the convex hull. In addition, there is a "buffer" method that attempts to return a polygon approximating the area within a given distance from the object. Note that, because the SFS does not yet define any geometric types with curved edges, most buffers will be polygonal approximations.
222
+
223
+ === 3.5. Size and Distance
224
+
225
+ Several size and distance calculations are available. You can compute the distance between two geometric objects, the length of a LineString, or the area of a Polygon. Note that there will be some cases when these computations don't make much sense due to the coordinate system.
226
+
227
+ p00 = factory.point(0, 0)
228
+ p20 = factory.point(2, 0)
229
+ p11 = factory.point(1, 1)
230
+ p00.distance(p11) # returns 1.41421356...
231
+ line = factory.line(p00, p20)
232
+ line.length # returns 2
233
+ line.distance(p11) # returns 1
234
+ ring = factory.linear_ring([p00, p11, p20, p00])
235
+ ring.length # returns 4.82842712...
236
+ ring.distance(p11) # returns 0
237
+ poly = factory.polygon(ring)
238
+ poly.area # returns 1
239
+
240
+ === 3.6. Serialization
241
+
242
+ The SFS defines two serialization schemes for geometric objects, known as the WKT (well-known text) and WKB (well-known binary) formats. The WKT is often used for textual display and transmission of a geometric object, while the WKB is sometimes used as an internal data format by spatial databases. Geometric objects in \RGeo define the <tt>as_text</tt> and <tt>as_binary</tt> methods to serialize the object into a data string, while \RGeo factories provide <tt>parse_wkt</tt> and <tt>parse_wkb</tt> methods to reconstruct geometric objects from their serialized form.
243
+
244
+ p00 = factory.point(0, 0)
245
+ p00.as_text # returns "Point(0 0)"
246
+ p10 = factory.point(1, 0)
247
+ line = factory.line(p00, p10)
248
+ line.as_text # returns "LineString(0 0, 1 0)"
249
+ p = factory.parse_wkt('POINT(3 4)')
250
+ p.x # returns 3
251
+
252
+ Note that there are several key shortcomings in the WKT and WKB formats as strictly defined by the SFS. In particular, neither format has official support for Z or M coordinates, and neither provides a way to specify the coordinate system (i.e. spatial reference ID) in which the object is represented. Because of this, variants of these formats have been developed. The most important to know are probably the EWKT and EWKB (or "extended" well-known formats) used by the PostGIS database, which supports Z and M as well as SRID. More recent versions of the SFS also have defined extensions to handle Z and M coordinates, but do not embed an SRID. \RGeo supports parsing and generating these variants through the RGeo::WKRep module.
253
+
254
+ == 4. Coordinate Systems and Projections
255
+
256
+ So far, we have discussed geometric data and operations mostly without reference to coordinate system. However, coordinate system is a critical component for interpreting what a piece of data means. If you have a point (1, 2), are the 1 and 2 measured in meters? Miles? Degrees? And where are they measured from? What is the origin (0, 0), and what directions do X and Y represent?
257
+
258
+ Generally, the spatial technologies we're discussing are used to represent location, objects on the earth's surface. In this section, we'll cover the coordinate systems that you'll use for geolocation, as well as the issues you'll face.
259
+
260
+ === 4.1. The World is Not Flat
261
+
262
+ First off, most of us agree that the earth is not flat, but has a shape resembling a slightly flattened ball. This immediately results in a whole host of complications when dealing with geometric objects on the earth's surface. Distance can't quite be computed accurately using the familiar formulas you learned in high school geometry. Lines that start off parallel will eventually cross. And if you try to display things on a flat computer monitor, you will end up with various kinds of distortion.
263
+
264
+ Let's take a simple example. Suppose we have a simple LineString with two points: starting in Vancouver, Canada, and ending in Frankfurt, Germany, both located at around 50 degrees north latitude. The LineString would consist of a straight line between those two points. But what is meant by "straight"? Does the shape follow the 50 degrees north latitude line, passing through Newfoundland? Or does it follow the actual shortest path on the globe, which passes much further north, close to Reykjavik, Iceland? (For a detailed explanation, see Morten Nielsen's post {"Straight Lines on a Sphere"}[http://www.sharpgis.net/post/2008/01/12/Straight-lines-on-a-sphere.aspx], which also includes some helpful diagrams.) If you were to call the SFS "distance" function to measure the distance between this LineString and a Point located at Reykjavik, what would you get?
265
+
266
+ The answer is, it depends on the coordinate system you're using.
267
+
268
+ === 4.2. Geographic Coordinates And Projections
269
+
270
+ GIS systems typically represent locations in one of three different types of coordinate systems: geocentric coordinates, geographic coordinates, and projected coordinates. Geocentric coordinate systems typically locate the origin of a three-dimensional Cartesian coordinate system at the center of the earth. They are not commonly used in an application-level interface, but are often a convenient coordinate system for running computational geometry algorithms. Geographic coordinates use the familiar latitude and longitude measurements, and sometimes also include altitude. These coordinates use a curved surface, either a sphere or an ellipsoid, as the domain, and perform geometric calculations on that non-flat domain. Projected coordinates involve transforming the curved domain of the earth's surface onto a flat domain such as a map display. Projected coordinates, then, are actually X-Y coordinates on the map itself: for example, pixel coordinates.
271
+
272
+ In our Vancouver to Frankfurt example, the path of a "straight" line is defined by the type of coordinate system being used. A geocentric coordinate system operates in 3-d space, and so a straight line will actually pass through the interior of the earth. A straight line in a geographic coordinate system (latitudes and longitudes) is typically defined as a geodesic, the shortest path between two points along the earth's surface. This path will take the line near Reykjavik. In a projected coordinate system, a straight line is defined as a straight line on the projection-- that is, a straight line drawn on the flat map. Google and Bing maps use a Mercator projection, in which lines of latitude are straight horizontal lines, so the LineString will follow the 50 degrees latitude line, passing through Newfoundland.
273
+
274
+ It is important to note that the actual _shape_ of the geometry is different depending on the coordinate system. If you "project" the geodesic (the straight Line in geographic coordinates) into a Mercator-projected Google map, the line will be curved. Therefore, the coordinate system is an integral part of the geometric object. If you get your coordinate systems mixed up, you may get incorrect results when you run a database query or a geometric operation. Morten Nielsen gives an example in {this post}[http://www.sharpgis.net/post/2009/02/06/Why-EPSG4326-is-usually-the-wrong-e2809cprojectione2809d.aspx].
275
+
276
+ flat_factory = RGeo::Geos.factory
277
+ curved_factory = RGeo::Geographic.spherical_factory
278
+ flat_line = flat_factory.line(flat_factory.point(0, 0),
279
+ flat_factory.point(1, 1))
280
+ curved_line = curved_factory.line(curved_factory.point(0, 0),
281
+ curved_factory.point(1, 1))
282
+ # flat_line and curved_line represent different shapes!
283
+
284
+ \RGeo provides access to different coordinate systems via its factories, as we saw earlier. If you are implementing geolocation, you will typically use one of the geographic factories which represent coordinates as latitude and longitude. Note, however, that \RGeo provides both projected and non-projected geographic factories. Projected factories are tied to a particular projection, representing coordinates as latitude and longitude, but doing calculations in the projection. If you use the simple mercator factory, a projected factory, the line between Vancouver and Frankfurt will follow the 50 degree latitude line, and so it will intersect a polygon representing Newfoundland. Non-projected factories perform calculations on a curved earth. Using the spherical factory, a non-projected factory that assumes the earth is a sphere, the line between Vancouver and Frankfurt will intersect a polygon representing Greenland. (In the future, \RGeo should also provide an ellipsoidal factory that performs the more complex calculations needed to model the earth as a _flattened_ sphere. This feature is not available yet.)
285
+
286
+ Some database systems behave similarly. PostGIS, for example, provides separate "geometry" and "geography" types. The former assumes a flat domain: it will behave similarly to \RGeo's simple mercator factory for a horizontal line (though for an oblique line, it will behave differently since the vertical axis is nonuniform in the Mercator projection.) The PostGIS "geography" type, however, operates on a curved earth, similar to \RGeo's spherical factory.
287
+
288
+ Does this matter in your application? The answer is, it depends: on what kind of data you have, how it will be analyzed or displayed, and how much accuracy you need. For a simple application that displays point locations or small-scale paths and areas on a Google map, you can probably safely ignore the issue. It would probably be easiest to simply use \RGeo's non-projected spherical factory, and the "geography" type if you are using PostGIS. The Google maps system automatically transforms your latitude-longitude coordinates into projected map coordinates when it displays markers and polygons. Remember the Vancouver to Frankfurt problem, however: if you display a line or polygon that is very large, the straight sides as displayed on the Google map may not be straight on the actual curved earth, and a LineString represented as a non-projected geographic object should not appear straight on the map. If your objects cover large areas of the globe, or for better accuracy or more sophisticated applications, you will need to pay explicit attention to projections.
289
+
290
+ === 4.3. Geodetic and Projection Parameters
291
+
292
+ This subsection covers some more advanced topics that most developers may not need to deal with directly, but I believe it is important to have at least a high-level understanding of them.
293
+
294
+ Simply put, there's more to a coordinate system than just the type: geocentric, geographic, or projected. For a geocentric coordinate system, we know it's centered at the center of the earth, but where _is_ the center of the earth? Which direction do the axes point? And do we measure the units in meters, miles, or light-years? For a geographic coordinate system, again, we need a center and orientation (i.e. where is the "zero longitude" line?), but we also need to define specifically _which_ "latitude". The latitude commonly used is the "geodetic latitude", which is the angle between the equator and what is normal (i.e. vertical) to the surface of the earth. This means it is dependent on one's model of the earth's surface, whether you use a sphere or a flattened ellipsoid, and how much flattening you choose. The same location on the earth's surface may have different latitudes depending on which system you use! As for projected systems, not only do we need to specify which projection to use (and there are hundreds defined), but we also need to know which geographic (latitude-longitude) system to start from. That is, a map projection is merely a function mapping latitude/longitude to flat coordinates, so we need to specify _which_ latitude/longitude.
295
+
296
+ To completely specify a coordinate system, then, a number of parameters are involved. Below I briefly describe the major parameters and what they mean:
297
+
298
+ *Ellipsoid*: (Also called a *sphereoid*) An ellipsoid is an approximation of the shape of the earth, defined by the length of the <b>semi-major axis</b>, or the radius at the equator (measured in meters) and the <b>inverse flattening</b> ratio, defined as the ratio between the semi-major axis, and the difference between the semi-major and semi-minor axes. Note that the earth is not a true ellipsoid, both because the gravitational and centrifugal bulging is not solved exactly by an ellipsoid, and because of local changes in gravity due to, for example, large mountain ranges. However, an ellipsoid is commonly used for cartographic applications. The ellipsoid matters because it defines how latitude is measured and what path a straight line will take.
299
+
300
+ *Datum*: This is a reference location against which measurements are made. There are generally two types of datums: horizontal datums, which define horizontal (e.g. latitude-longitude) coordinate systems, and vertical datums, which define the "zero altitude" point against which altitude measurements are made.
301
+
302
+ The most common datum in use, and generally the one you will encounter most often when writing location applications, is *WGS84*. This datum comprises both horizontal and vertical parts. The WGS84 horizontal datum defines the latitudes and longitudes used by the global positioning system (GPS). Latitudes are defined using the WGS84 reference ellipsoid, using the standard geodetic definition of the angle from the normal to the ellispoid. Longitudes are measured relative to the *prime meridian*. (Interestingly, the WGS84 prime meridian is not exactly on the historical Greenwich prime meridian that passes through the Royal Observatory. A shift of about 100 meters to the east was made in order to make WGS84 more closely match a commonly-used North America-specific datum.)
303
+
304
+ The WGS84 vertical datum is a bumpy ellipsoid representing nominal sea level for all parts of the earth. This shape is also known as the *geoid*.
305
+
306
+ A geographic coordinate system is generally defined by a datum and a prime meridian. A projected coordinate system starts from a geographic coordinate system and includes additional parameters specific to the projection.
307
+
308
+ Which coordinate system should you use for location data? The usual simple answer is WGS84. Because this is the same worldwide geographic coordinate system that GPS uses, as do most geocoding services and so forth, most of the data you will encounter will be in WGS84. Only in certain locale-specific cases might a different coordinate system be in common use. However, remember that WGS84 is a geographic coordinate system, not a projected coordinate system. It does not make sense to say that your map uses WGS84, because chances are your map is a flat map, not a map wrapped around the WSG84 ellipsoidal shape.
309
+
310
+ What about Google and Bing maps? They use a combination of two coordinate systems with a projection. The API inputs take latitude and longitude in the WGS84 coordinate system. However, the maps themselves are Mercator projections (with a minor modification to make computations easier), and so the map implementations transform the coordinates internally. Again, Morten Nielsen provides a more {detailed description}[http://www.sharpgis.net/post/2007/07/27/The-Microsoft-Live-Maps-and-Google-Maps-projection.aspx].
311
+
312
+ === 4.4. Using Proj4
313
+
314
+ Under most circumstances, when you're working with geographic data in \RGeo, you can probably use either the spherical factory (if you just want to deal with points on the sphere) or the simple mercator factory (if you want to deal with objects as they would appear on Google or Bing maps). However, if you need to work with arbitrary projections, you can use {Proj}[http://trac.osgeo.org/proj/], a C library that understands coordinate systems and handles the math involved. Proj defines a syntax for specifying coordinate systems based on the above parameters, and provides an implementation for transforming coordinates from one coordinate system to another. Its syntax has become a <i>de facto</i> standard.
315
+
316
+ \RGeo provides a Ruby wrapper around the Proj interface, and integrates proj into its representation of geometric objects. You can use the Proj syntax to specify the coordinate system for input data, and tell \RGeo to translate it to a different coordinate system for analysis or output. \RGeo's geographic factories can also be configured with a particular projection using the Proj syntax, and automatically convert between latitude-longitude and that projection's coordinate system. For example:
317
+
318
+ # Geographic factory that projects to a world mercator projection.
319
+ # Note the ellps and datum set to WGS84.
320
+ factory = RGeo::Geographic.projected_factory(:projection_proj4 =>
321
+ '+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs')
322
+
323
+ # Create a point with the long/lat of Seattle
324
+ point = factory.point(-122.3, 47.6)
325
+
326
+ # Project the point to mercator projection coordinates.
327
+ # The coordinates are in "meters" at the equator.
328
+ point.projection.as_text # => "POINT (-13614373.724017357 6008996.432357133)"
329
+
330
+ === 4.5. Spatial Reference Systems and the EPSG Dataset
331
+
332
+ The OGC also defines a syntax for specifying coordinate systems, the {Coordinate Transformation Services Specification}[http://www.opengeospatial.org/standards/ct]. This specification defines both an object model and a well-known text representation for coordinate systems and transformations. \RGeo also provides basic support for this specification; you can attach an OGC coordinate system specification to a factory to indicate the coordinate system. However, if you want \RGeo to automatically convert spatial data between coordinate systems, you must use Proj4 syntax.
333
+
334
+ Finally, there also exists a <i>de facto</i> standard database of coordinate systems and related parameters, published by EPSG (now the OGP Geomatics Committee). This is a set of coordinate systems, each tagged with a well-known ID number, including geographic and projected systems. You can browse this database, including both OGC and Proj4 representations, at http://www.spatialreference.org/. This database is also included as a table in many popular spatial databases including PostGIS and SpatiaLite. Typically, the EPSG number is used as the SRID identifying the coordinate system for geometric objects stored in the database.
335
+
336
+ The most common EPSG number in use is 4326, which identifies the WGS84 geographic (longitude-latitude) coordinate system on the WGS84 ellipsoid. In fact, current versions of PostGIS restrict geography objects to this SRID. Be aware, again, that this is a non-projected ellipsoidal coordinate system. If you load EPSG 4326 data directly into, say, a Cartesian implementation in \RGeo, then the basic data fields will work fine, but size and distance functions, as well as spatial analysis operations such as intersections, may yield surprising or incorrect results because the data's coordinate system does not match how \RGeo is treating it.
337
+
338
+ The EPSG database is so ubiquitous that it is commonly distributed along with spatial database systems such as PostGIS and SpatiaLite, as described in the next section. \RGeo takes advantage of this by providing automatic lookup of the coordinate system from the EPSG number. If you use one of these spatial databases, you do not need to know the exact definition of the coordinate system; just provide the EPSG number in the SRID field, and the \RGeo factory will know how to construct the correct Proj4 syntax for performing coordinate conversions. You can even create custom spatial reference system databases if you do not want to use the provided standard databases.
339
+
340
+ == 5. Spatial Databases
341
+
342
+ Now that we have a basic understanding of geospatial data, we'll turn to the question of storing and querying for this data.
343
+
344
+ As we have seen, there exist a variety of ways to serialize geometric objects, notably the OGC "well-known text" and "well-known binary" formats. The simplest way to store such objects in a database, then, is to simply serialize the object into a blob. However, this would not allow us to perform queries with conditions relating to the object itself. Typical location-based applications may need to run queries such as "give me all the locations within one mile of a particular position." This kind of capability is the domain of spatial databases.
345
+
346
+ === 5.1. Spatial Queries and Spatial Indexes
347
+
348
+ The OGC defines a {specification}[http://www.opengeospatial.org/standards/sfs], related to the SFS, describing SQL extensions for a spatial database. This specification includes a table for spatial reference systems (that is, coordinate systems) which can contain OGC and Proj4 representations, and a table of metadata for geometry columns which stores such information as type, dimension, and srid constraints. It also defines a suite of SQL functions that you can call in a query. For example, in a compliant database, to find all rows in "mytable" where the geometry-valued column "geom" contains data within 5 units of the coordinates (10, 20), you might be able to run a query similar to:
349
+
350
+ SELECT * FROM mytable WHERE ST_Distance(geom, ST_WKTToSQL("POINT(10 20)")) > 5;
351
+
352
+ Like all database queries, however, when there are a large number of rows, such a query can be slow if it has to do a full table scan. This is especially true if it has to evaluate geometric functions like the above, which can be numerically complex and slow to execute. To speed up queries, it is necessary to index your spatial columns.
353
+
354
+ Spatial indexes are somewhat more complex than typical database indexes. A typical B-tree index relies on a global ordering of data: the fact that you can sort scalar values in a binary tree and hence perform logarithmic-time searches. However, there isn't an obvious global ordering for spatial data. Should POINT(0 1) come before or after POINT(1 0)? And how do each of those compare with LINESTRING(0 1, 1 0)? More concretely, spatial data exists in two dimensions rather than one, and can span finite ranges.
355
+
356
+ Spatial databases handle the problem of indexing spatial data in various ways, but most techniques are variants on an indexing algorithm known as an R-tree. I won't go into the details of how an R-tree works here. For the interested, I recommend the text {"Spatial Databases With Application To GIS"}[http://www.amazon.com/dp/1558605886], which covers a wide variety of issues related to basic spatial database implementation. For our purposes, just note that for large datasets, it is necessary to index the geometry columns, and that the index creation process may be different from that of normal scalar columns. The next sections provide some information specific to some of the common spatial databases.
357
+
358
+ === 5.2. MySQL Spatial
359
+
360
+ {MySQL}[http://mysql.com/] maintains its dual reputation as probably the easiest of the open source relational databases to manage, but as tending to miss some features and standards compliance. Recent versions of MySQL have spatial column support baked in, and they are extremely easy to use: just create a column of type GEOMETRY, or POINT, or LINESTRING, or any of the OGC types. You can also create spatial indexes simply by adding the SPATIAL modifier to CREATE INDEX. Note, however, that as of MySQL 5.1, only MyISAM tables support spatial indexes. If you're using Innodb, you can add spatial columns but you can't index them.
361
+
362
+ CREATE TABLE mytable(id INT NOT NULL PRIMARY KEY, latlon POINT) ENGINE=MyISAM;
363
+ CREATE SPATIAL INDEX idx_latlon ON mytable(latlon);
364
+
365
+ MySQL represents data internally using a variant of WKB which has been slightly modified by prepending 4 extra bytes to store an object's SRID. You can interpret this data directly, or use provided functions to convert to and from WKB and WKT.
366
+
367
+ What MySQL lacks is support for most of the advanced spatial geometry functions such as the relational operators (e.g. ST_Intersects(), ST_Contains(), etc.), so you will not be able to perform complex geometric calculations using the database engine. You'll have to just load the data into your application and use \RGeo to perform those calculations. It also does not provide the OGC-specified geometry columns or spatial reference system tables. Because the former table is not present, you will not be able to specify constraints on your geometries beyond the type: you will not be able to constrain the SRID for a column, and columns will not be able to support Z and M coordinates. Because the latter table is not present, MySQL cannot perform coordinate system transformations itself, nor provide you with the standard EPSG dataset for your own use.
368
+
369
+ In general, if you're using MySQL for the rest of your application, and you just need simple geospatial data capabilities, it's very easy to use MySQL spatial. Just make sure your table uses the MyISAM engine if you need to index the geometry.
370
+
371
+ === 5.3. SpatiaLite
372
+
373
+ {SpatiaLite}[http://www.gaia-gis.it/spatialite/] is an add-on library for the popular database {Sqlite}[http://www.sqlite.org/]. It is close to a fully compliant implementation of the OGC SQL specification, but as a result it is more difficult to manage than MySQL.
374
+
375
+ To install SpatiaLite, you must compile it as a shared library, then load that library as an extension using the appropriate sqlite API. This gives you access to the full suite of spatial SQL functions defined by the OGC, along with a set of utility functions for managing spatial columns and indexes as well as the geometry columns and spatial reference systems tables. These utility functions automatically create and manage the appropriate entries in the geometry columns table and the triggers that enforce type and SRID constraints and maintain the spatial indexes.
376
+
377
+ CREATE TABLE mytable(id INTEGER PRIMARY KEY);
378
+ SELECT AddGeometryColumn('mytable', 'latlon', 4326, 'POINT', 'XY');
379
+ SELECT CreateSpatialIndex('mytable', 'latlon');
380
+
381
+ Spatial indexes themselves are implemented using SpatiaLite's Rtree extension, and so queries that use a spatial index are a little more complex to write. You have to join to the Rtree table, or use a nested query:
382
+
383
+ SELECT * FROM mytable WHERE id IN
384
+ (SELECT pkid FROM idx_mytable_latlon WHERE
385
+ xmin > -123 AND xmax < -122 AND ymin > 47 AND ymax < 48);
386
+
387
+ SpatiaLite's internal format is a binary format loosely based on the WKB, but extended to include the SRID, as well as some internal bounding rectangle data to speed up calculations. Like MySQL, SpatiaLite provides functions to convert this data to and from WKB and WKT. I'm not completely clear on this, but it does not seem that SpatiaLite supports Z and M coordinates at this time.
388
+
389
+ === 5.4. PostGIS
390
+
391
+ As {PostgreSQL}[http://www.postgresql.org/] is the most complete (and complex) open-source relational database, so the {PostGIS}[http://www.postgis.org/] add-on library is also the most complete and complex of the spatial databases I discuss here. It is a highly compliant implementation of the OGC SQL specification, including all the needed functions and tables. It also supports spatial indexes using PostgreSQL's GiST (generalized search tree) index implementation.
392
+
393
+ Like SpatiaLite, PostGIS is compiled and installed as a plug-in library to the database. Also like SpatiaLite, PostGIS provides a suite of utility functions for managing spatial columns and updating the geometry columns table. PostGIS indexes, however, are built in, and so use the more traditional index creation syntax. Like SpatiaLite, PostGIS provides a spatial reference systems table from which you can look up EPSG codes and obtain Proj4 and OGC representations of coordinate systems.
394
+
395
+ CREATE TABLE mytable(id INTEGER PRIMARY KEY);
396
+ SELECT AddGeometryColumn('mytable', 'latlon', 4326, 'POINTM', 3);
397
+ CREATE INDEX idx_mytable_latlon ON mytable USING GIST (latlon);
398
+
399
+ PostGIS actually provides two types of spatial columns: geometry and geography. The former assumes a flat coordinate system and performs Euclidean/Cartesian geometric calculations using any EPSG coordinate system you choose. The latter is specifically designed for geographic (longitude-latitude) coordinate systems. It performs calculations on the sphereoid and only works with EPSG 4326; however, some of the more advanced functions are not implemented because the calculations involved would be prohibitively complex. When you create a spatial column in PostGIS, you will need to decide whether to use the geometry or geography type.
400
+
401
+ PostGIS uses as its internal format a variant of WKB known as EWKB. This variant provides support for Z and M coordinates as well as embedded SRID values. PostGIS also defines an corresponding EWKT format adding Z and M support and SRIDs to WKT, and provides conversion functions. The EWKB and EWKT variants are commonly used and so are supported by \RGeo's WKB and WKT parsers.
402
+
403
+ === 5.5. \RGeo ActiveRecord Integration
404
+
405
+ \RGeo provides extra support for web applications built on Ruby On Rails or similar frameworks that use the ActiveRecord library, in the form of a suite of ActiveRecord connection adapters for the various spatial databases we have covered. These connection adapters subclass the stock database connection adapters, and add support for the \RGeo geometric data types. You can create spatial columns and indexes using extensions to the ActiveRecord schema definition functions, and the appropriate fields on your ActiveRecord objects will appear as \RGeo spatial data objects. Some of these adapters also modify ActiveRecord's rake tasks to help automate the process of creating and maintaining spatial databases. Using one of these connection adapters is probably the easiest way to integrate your Rails application with a spatial database.
406
+
407
+ \RGeo's spatial ActiveRecord adapters are provided in separate gems, named according to the recommended convention. These are the names of these connection adapters:
408
+
409
+ * *mysqlspatial*: Subclasses the mysql adapter and adds support for MySQL's spatial types. Available as the <b>activerecord-mysqlspatial-adapter</b> gem.
410
+ * <b>mysql2spatial</b>: Subclasses the mysql2 adapter and adds support for MySQL's spatial types. Available as the <b>activerecord-mysql2spatial-adapter</b> gem.
411
+ * *spatialite*: Subclasses the sqlite3 adapter and adds support for the SpatiaLite extension. Available as the <b>activerecord-spatialite-adapter</b> gem.
412
+ * *postgis*: Subclasses the postgresql adapter and adds support for the PostGIS extension. Available as the <b>activerecord-postgis-adapter</b> gem.
413
+
414
+ == 6. Location Service Integration
415
+
416
+ When writing a location-aware application, you will often need to interact with external sources of data and external location-based services. \RGeo provides several tools to facilitate this data transfer.
417
+
418
+ === 6.1. Shapefiles and Spatial Data Sets
419
+
420
+ {ESRI}[http://www.esri.com/] is one of the oldest and most well-known GIS corporations, developing a suite of applications including the venerable {ArcGIS}[http://www.esri.com/software/arcgis/index.html]. ESRI also created the Shapefile, a geospatial data file format that has become a <i>de facto</i> standard despite its legacy and somewhat awkward characteristics. Today, many of the widely-available geospatial data sets are distributed as shapefiles.
421
+
422
+ The shapefile format is specified in an ESRI {whitepaper}[http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf]. It typically comprises three files: the main file "*.shp" containing the actual geometric data, a corresponding index file "*.shx" providing offsets into the .shp file to support random access, and a corresponding file "*.dbf" in dBASE format that stores arbitrary attributes for the shape records. Many GIS systems can read and/or write shapefiles. SpatiaLite can treat a shapefile as an external "virtual table". An optional \RGeo module, RGeo::Shapefile (distributed as the gem <b>rgeo-shapefile</b>) can read shapefiles and expose their contents as \RGeo's geometric data types.
423
+
424
+ === 6.2. GeoJSON and Location-Based Services
425
+
426
+ Location is becoming a common feature of web services as well. An emerging standard in the encoding of geographic data in a web service is {GeoJSON}[http://www.geojson.org/], an geospatial data format based on JSON. GeoJSON can encode any of the OGR geometry types, along with attributes, bounding boxes, and coordinate system specifications. {SimpleGeo}[http://www.simplegeo.com/] is one of several high-profile location based APIs now using GeoJSON. \RGeo also provides an optional module RGeo::GeoJSON (distributed as the gem <b>rgeo-geojson</b>) that can read and write this format.
427
+
428
+ Other common formats that may be used by web services include {GeoRSS}[http://www.georss.org/], an extension to RSS and Atom for geotagging RSS feed entries, and {KML}[http://www.opengeospatial.org/standards/kml], an XML-based markup language for geographic information originally developed by Google and recently adopted as an OGC standard. The GeoRuby library provides rudimentary support for these formats. \RGeo does not yet, but appropriate optional modules are on the to-do list.
429
+
430
+ == 7. Conclusion
431
+
432
+ Geospatial systems represent a rapidly growing and evolving area of technology, and helpful resources are often difficult to find outside of the relatively niche GIS community. Writing a robust location-aware application sometimes requires an understanding of a number of GIS concepts, tools, and specifications. We have covered a few of the most important ones in this document.
433
+
434
+ At the time of this writing, a number of open source geospatial tools and libraries are starting to mature. For Ruby and Rails developers, the \RGeo library is one of these emerging tools, and represents an important step towards supporting the next generation of geospatial applications.
435
+
436
+ == History
437
+
438
+ * Version 0.1 / 5 Dec 2010: Initial draft
439
+ * Version 0.2 / 7 Dec 2010: More code examples and other minor updates
440
+ * Version 0.3 / 17 Feb 2011: Further minor clarifications and fixes, and coverage of features in newer \RGeo releases