rgeo 0.1.21 → 0.1.22
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +11 -0
- data/README.rdoc +1 -1
- data/Spatial_Programming_With_RGeo.rdoc +382 -0
- data/Version +1 -1
- data/lib/active_record/connection_adapters/postgis_adapter.rb +1 -1
- data/lib/rgeo.rb +4 -4
- data/lib/rgeo/active_record/base_modifications.rb +1 -1
- data/lib/rgeo/all.rb +1 -1
- data/lib/rgeo/cartesian/bounding_box.rb +2 -2
- data/lib/rgeo/cartesian/factory.rb +18 -11
- data/lib/rgeo/cartesian/interface.rb +4 -4
- data/lib/rgeo/coord_sys.rb +8 -1
- data/lib/rgeo/coord_sys/proj4.rb +4 -4
- data/lib/rgeo/feature/factory.rb +68 -49
- data/lib/rgeo/feature/factory_generator.rb +2 -2
- data/lib/rgeo/feature/types.rb +4 -4
- data/lib/rgeo/geo_json/coder.rb +5 -5
- data/lib/rgeo/{geography.rb → geographic.rb} +15 -15
- data/lib/rgeo/{geography → geographic}/factory.rb +27 -20
- data/lib/rgeo/{geography → geographic}/interface.rb +43 -29
- data/lib/rgeo/{geography → geographic}/proj4_projector.rb +2 -2
- data/lib/rgeo/{geography → geographic}/projected_feature_classes.rb +2 -2
- data/lib/rgeo/{geography → geographic}/projected_feature_methods.rb +2 -2
- data/lib/rgeo/{geography → geographic}/projected_window.rb +5 -5
- data/lib/rgeo/{geography → geographic}/simple_mercator_projector.rb +2 -2
- data/lib/rgeo/{geography → geographic}/spherical_feature_classes.rb +2 -2
- data/lib/rgeo/{geography → geographic}/spherical_feature_methods.rb +2 -2
- data/lib/rgeo/{geography → geographic}/spherical_math.rb +1 -1
- data/lib/rgeo/geos/factory.rb +22 -15
- data/lib/rgeo/geos/interface.rb +3 -3
- data/lib/rgeo/geos/zm_factory.rb +20 -6
- data/lib/rgeo/impl_helper/basic_point_methods.rb +2 -2
- data/lib/rgeo/shapefile/reader.rb +5 -5
- data/lib/rgeo/wkrep/wkb_generator.rb +2 -2
- data/lib/rgeo/wkrep/wkb_parser.rb +4 -4
- data/lib/rgeo/wkrep/wkt_generator.rb +2 -2
- data/lib/rgeo/wkrep/wkt_parser.rb +7 -7
- data/test/active_record/common_setup_methods.rb +1 -1
- data/test/geos/tc_point.rb +3 -3
- data/test/geos/tc_zmfactory.rb +7 -7
- data/test/{projected_geography → projected_geographic}/tc_geometry_collection.rb +2 -2
- data/test/{projected_geography → projected_geographic}/tc_line_string.rb +2 -2
- data/test/{projected_geography → projected_geographic}/tc_multi_line_string.rb +2 -2
- data/test/{projected_geography → projected_geographic}/tc_multi_point.rb +2 -2
- data/test/{projected_geography → projected_geographic}/tc_multi_polygon.rb +3 -3
- data/test/{projected_geography → projected_geographic}/tc_point.rb +5 -5
- data/test/{projected_geography → projected_geographic}/tc_polygon.rb +2 -2
- data/test/shapefile/tc_shapelib_tests.rb +10 -10
- data/test/simple_cartesian/tc_point.rb +3 -3
- data/test/simple_mercator/tc_geometry_collection.rb +1 -1
- data/test/simple_mercator/tc_line_string.rb +1 -1
- data/test/simple_mercator/tc_multi_line_string.rb +1 -1
- data/test/simple_mercator/tc_multi_point.rb +1 -1
- data/test/simple_mercator/tc_multi_polygon.rb +2 -2
- data/test/simple_mercator/tc_point.rb +4 -4
- data/test/simple_mercator/tc_polygon.rb +1 -1
- data/test/simple_mercator/tc_window.rb +39 -39
- data/test/spherical_geographic/tc_calculations.rb +203 -0
- data/test/{spherical_geography → spherical_geographic}/tc_geometry_collection.rb +2 -2
- data/test/{spherical_geography → spherical_geographic}/tc_line_string.rb +2 -2
- data/test/{spherical_geography → spherical_geographic}/tc_multi_line_string.rb +2 -2
- data/test/{spherical_geography → spherical_geographic}/tc_multi_point.rb +2 -2
- data/test/{spherical_geography → spherical_geographic}/tc_multi_polygon.rb +3 -3
- data/test/{spherical_geography → spherical_geographic}/tc_point.rb +6 -6
- data/test/{spherical_geography → spherical_geographic}/tc_polygon.rb +2 -2
- data/test/tc_geojson.rb +3 -3
- data/test/tc_oneoff.rb +5 -5
- data/test/wkrep/tc_wkb_generator.rb +3 -3
- data/test/wkrep/tc_wkb_parser.rb +16 -16
- data/test/wkrep/tc_wkt_generator.rb +3 -3
- data/test/wkrep/tc_wkt_parser.rb +21 -21
- metadata +46 -44
- data/test/spherical_geography/tc_calculations.rb +0 -203
data/History.rdoc
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
=== 0.1.22 / 2010-12-05
|
2
|
+
|
3
|
+
This should be the last pre-alpha development version. The next version planned is the 0.2 alpha release.
|
4
|
+
|
5
|
+
* API CHANGE: Renamed Geography module to Geographic.
|
6
|
+
* API CHANGE: Renamed Factory#has_capability? to Factory#property to generalize the API.
|
7
|
+
* API CHANGE: Factory#proj4 and Factory#coord_sys are now required methods.
|
8
|
+
* The ZM Geos factory didn't properly handle proj4. Fixed.
|
9
|
+
* The proj4-based projected geographic factory now extracts the cooresponding geographic coordinate system from the projection, rather than always using WGS84.
|
10
|
+
* Initial draft of Spatial Programming paper.
|
11
|
+
|
1
12
|
=== 0.1.21 / 2010-12-03
|
2
13
|
|
3
14
|
* API CHANGE: Added "_factory" to the end of the Geography toplevel interface methods, for consistency with the rest of the API.
|
data/README.rdoc
CHANGED
@@ -99,9 +99,9 @@ installation prefix directory using the "--with-proj-dir" option.
|
|
99
99
|
|
100
100
|
This is our planned roadmap, in rough priority order.
|
101
101
|
|
102
|
+
* OGC coordinate system representation.
|
102
103
|
* Continued work on the ActiveRecord adapters.
|
103
104
|
* Possible Arel extensions for constructing spatial queries.
|
104
|
-
* OGC coordinate system representation.
|
105
105
|
* Support for bbox and crs elements of GeoJSON.
|
106
106
|
* Ellipsoidal geography implementation, probably utilizing geographiclib.
|
107
107
|
* Integration with certain third-party services such as SimpleGeo.
|
@@ -0,0 +1,382 @@
|
|
1
|
+
= An Introduction to Spatial Programming With RGeo
|
2
|
+
|
3
|
+
* by Daniel Azuma
|
4
|
+
* version 0.1 (5 Dec 2010)
|
5
|
+
|
6
|
+
== Introduction
|
7
|
+
|
8
|
+
=== About This Document
|
9
|
+
|
10
|
+
Spatial programming and location-based services are an important emerging technology. Once the exclusive domain of complex GIS systems, these techniques and features are now increasingly available in small applications, websites, and enterprises. This document provides a brief overview of techniques and tools used to implement these location-aware application features, focusing on the Ruby programming language and an open-source technology stack. We will cover both the key technical concepts and the necessary software components.
|
11
|
+
|
12
|
+
The contents of this document are as follows.
|
13
|
+
|
14
|
+
* Section 1 is a short introduction to geospatial programming, 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 external location data and services.
|
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 that want more detailed information.
|
22
|
+
|
23
|
+
=== About The Author
|
24
|
+
|
25
|
+
{Daniel Azuma}[http://www.daniel-azuma.com/] is 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, computational geometry, software architecture, and large scale web services. 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 technologies. 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 of startups up to the giants such as Facebook and Twitter. Although the underlying disciplines of 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 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. Working with location data requires a few techniques beyond the document-oriented or relational data typically used in a web application. The primary purpose of this document is to cover the basics that a Ruby developer needs to know when developing with location data, and to introduce the tools 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 such as {OpenLayers}[http://openlayers.org/] and {PolyMaps}[http://polymaps.org/] have also appeared, which let you serve your own map data.
|
40
|
+
|
41
|
+
Most major relational databases now support spatial extensions. The {MySQL}[http://mysql.com/] database provides partial spatial 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. {SimpleGeo}[http://www.simplegeo.com/] is a relatively new service 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 newer library 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 develop open standards and open interfaces for geospatial software systems to promote interoperability. Most of the specific concepts, data types, and operations in this document were developed 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 de facto 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 spatial 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 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] introduces a hack into a few of the popular \ActiveRecord database adapters to support spatial columns in the database.
|
52
|
+
|
53
|
+
In this document, we will use {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
|
+
== 2. Spatial Data Types
|
56
|
+
|
57
|
+
=== 2.1. The Simple Features Specification
|
58
|
+
|
59
|
+
The Open Geospatial Consortium (OGC) defines and publishes a {standard}[http://www.opengeospatial.org/standards/sfa] entitled "Geographic information -- Simple feature access", 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 interfaces used by most spatial applications and databases. Although more recent versions are now available, most current implementations, including \RGeo, follow version 1.1 of the SFS, and this is the specification we will cover here.
|
60
|
+
|
61
|
+
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.
|
62
|
+
|
63
|
+
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.
|
64
|
+
|
65
|
+
=== 2.2. Coordinates
|
66
|
+
|
67
|
+
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.
|
68
|
+
|
69
|
+
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.
|
70
|
+
|
71
|
+
=== 2.3. Point
|
72
|
+
|
73
|
+
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.
|
74
|
+
|
75
|
+
=== 2.4. LineString
|
76
|
+
|
77
|
+
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 together with line segments, and you can retrieve those points from the LineString object.
|
78
|
+
|
79
|
+
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, where the two endpoints are the same point.
|
80
|
+
|
81
|
+
(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 Curve that is not a LineString.)
|
82
|
+
|
83
|
+
=== 2.5. Polygon
|
84
|
+
|
85
|
+
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.
|
86
|
+
|
87
|
+
(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 Surface other than Polygon.)
|
88
|
+
|
89
|
+
=== 2.6. MultiPoint
|
90
|
+
|
91
|
+
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.
|
92
|
+
|
93
|
+
=== 2.7. MultiLineString
|
94
|
+
|
95
|
+
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.
|
96
|
+
|
97
|
+
(MultiLineString is a subclass of the non-instantiable abstract class MultiCurve.)
|
98
|
+
|
99
|
+
=== 2.8. MultiPolygon
|
100
|
+
|
101
|
+
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.
|
102
|
+
|
103
|
+
(MultiPolygon is a subclass of the non-instantiable abstract class MultiSurface.)
|
104
|
+
|
105
|
+
=== 2.9. Geometry and GeometryCollection
|
106
|
+
|
107
|
+
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.
|
108
|
+
|
109
|
+
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 that are not commonly available in implementations. That additional information may be confusing.
|
110
|
+
|
111
|
+
=== 2.10. \RGeo Geometry Factories
|
112
|
+
|
113
|
+
The data types we have covered here are actually merely interface specifications. \RGeo actually provides several different implementations of these data types, for different use cases. For example, the RGeo::Geos implementation is \RGeo's main implementation which provides all these data types and all the operations 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.
|
114
|
+
|
115
|
+
A particular implementation of these spatial data types is represented in \RGeo by a factory. A factory simply represents the coordinate system and other settings for that implementation. You can then create actual geometric objects by calling methods on that factory, as defined in the RGeo::Feature::Factory interface. For example:
|
116
|
+
|
117
|
+
point1 = factory.point(1, 2)
|
118
|
+
point2 = factory.point(3, -1)
|
119
|
+
line = factory.line_string([point1, point2])
|
120
|
+
|
121
|
+
The most basic 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:
|
122
|
+
|
123
|
+
factory = ::RGeo::Cartesian.preferred_factory
|
124
|
+
|
125
|
+
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:
|
126
|
+
|
127
|
+
factory = ::RGeo::Geographic.simple_mercator_factory
|
128
|
+
|
129
|
+
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.:
|
130
|
+
|
131
|
+
factory = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true)
|
132
|
+
factory.property(:has_z_coordinate) # returns true
|
133
|
+
factory.property(:has_m_coordinate) # returns false
|
134
|
+
point = factory.point(1, 2, 3) # this point has a Z coordinate
|
135
|
+
|
136
|
+
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.
|
137
|
+
|
138
|
+
== 3. Spatial Operations
|
139
|
+
|
140
|
+
In addition to representing geometric data, the SFS interfaces define a suite of basic operations on this data, defines as methods on the object interfaces. 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.
|
141
|
+
|
142
|
+
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, only implements a handful of the defined functions.
|
143
|
+
|
144
|
+
=== 3.1. Basic Properties
|
145
|
+
|
146
|
+
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.
|
147
|
+
|
148
|
+
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.
|
149
|
+
|
150
|
+
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.
|
151
|
+
|
152
|
+
=== 3.2. Equality and Comparisons
|
153
|
+
|
154
|
+
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.
|
155
|
+
|
156
|
+
if polygon1.overlaps?(polygon2)
|
157
|
+
# do something
|
158
|
+
end
|
159
|
+
|
160
|
+
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:
|
161
|
+
|
162
|
+
1 == 1 # => true
|
163
|
+
1 == 1.0 # => true (because the values are the same)
|
164
|
+
1.eql?(1) # => true
|
165
|
+
1.eql?(1.0) # => false (because the classes are different)
|
166
|
+
a = "foo"
|
167
|
+
b = "foo"
|
168
|
+
a == b # => true
|
169
|
+
a.eql?(b) # => true
|
170
|
+
a.equals?(b) # => false (because they are different objects)
|
171
|
+
a.equals?(a) # => true
|
172
|
+
|
173
|
+
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).
|
174
|
+
|
175
|
+
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.
|
176
|
+
|
177
|
+
p1 = factory.point(1, 1)
|
178
|
+
p2 = factory.point(1, 1)
|
179
|
+
mp = factory.multi_point([p1])
|
180
|
+
p1 == p2 # => true
|
181
|
+
p1.equal?(mp) # => true
|
182
|
+
p1 == mp # => true
|
183
|
+
p1.eql?(mp) # => false
|
184
|
+
p1.eql?(p2) # => true
|
185
|
+
p1.equals?(p2) # => false
|
186
|
+
p1.equals?(p1) # => true
|
187
|
+
|
188
|
+
=== 3.3. Combinations
|
189
|
+
|
190
|
+
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.
|
191
|
+
|
192
|
+
mp = point1.union(point2)
|
193
|
+
# or
|
194
|
+
mp = point1 + point2
|
195
|
+
|
196
|
+
=== 3.4. Boundaries, Envelopes, etc.
|
197
|
+
|
198
|
+
Methods exist 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.
|
199
|
+
|
200
|
+
=== 3.5. Size and Distance
|
201
|
+
|
202
|
+
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.
|
203
|
+
|
204
|
+
=== 3.6. Serialization
|
205
|
+
|
206
|
+
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.
|
207
|
+
|
208
|
+
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 formats through the RGeo::WKRep module.
|
209
|
+
|
210
|
+
== 4. Coordinate Systems and Projections
|
211
|
+
|
212
|
+
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?
|
213
|
+
|
214
|
+
Generally, the spatial technologies we're discussing are used to represent location, objects on the eart'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.
|
215
|
+
|
216
|
+
=== 4.1. The World is Not Flat
|
217
|
+
|
218
|
+
First off, most of us agree that the earth is not flat, but shaped as something 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.
|
219
|
+
|
220
|
+
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 the LineString and a Point located at Reykjavik, what would you get?
|
221
|
+
|
222
|
+
The answer is, it depends on the coordinate system you're using.
|
223
|
+
|
224
|
+
=== 4.2. Geographic Coordinates And Projections
|
225
|
+
|
226
|
+
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 geometric analysis 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.
|
227
|
+
|
228
|
+
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 the LineString will actually go 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 on 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.
|
229
|
+
|
230
|
+
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 query or a geometric analysis. Morten Nielsen gives an example in {this post}[http://www.sharpgis.net/post/2009/02/06/Why-EPSG4326-is-usually-the-wrong-e2809cprojectione2809d.aspx].
|
231
|
+
|
232
|
+
\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.)
|
233
|
+
|
234
|
+
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.
|
235
|
+
|
236
|
+
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. Just remember the Vancouver to Frankfurt problem: 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. For better accuracy or more sophisticated applications, you will need to pay explicit attention to projections.
|
237
|
+
|
238
|
+
=== 4.3. Geodetic and Projection Parameters
|
239
|
+
|
240
|
+
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 conceptual understanding of them.
|
241
|
+
|
242
|
+
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 what we mean by "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. A map projection is merely a function mapping latitude-longitude to flat coordinates, but again, we need to specify _which_ latitude/longitude.
|
243
|
+
|
244
|
+
To completely specify a coordinate system, then, a number of parameters are involved. Below I briefly describe the major parameters and what they mean:
|
245
|
+
|
246
|
+
*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.
|
247
|
+
|
248
|
+
*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.
|
249
|
+
|
250
|
+
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 the older (and North-America specific) NAD27 datum.
|
251
|
+
|
252
|
+
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*.
|
253
|
+
|
254
|
+
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.
|
255
|
+
|
256
|
+
Which coordinate system should you use? The simple answer is WGS84. This is the same worldwide geographic coordinate system that GPS uses, as do most maps, geocoding services, and so forth. Only in certain locale-specific cases might a different coordinate system be in 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 an ellipsoidal map.
|
257
|
+
|
258
|
+
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 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].
|
259
|
+
|
260
|
+
=== 4.4. Using Proj4
|
261
|
+
|
262
|
+
Under most circumstances, when you're working with geographic data, 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, there exists a software library that understands coordinate systems and handles the math involved. {Proj}[http://trac.osgeo.org/proj/] is a C library that defines a syntax for specifying coordinate systems based on the above parameters, and an implementation for transforming coordinates from one coordinate system to another. Its syntax has become a de facto standard.
|
263
|
+
|
264
|
+
\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:
|
265
|
+
|
266
|
+
# Geographic factory that projects to a world mercator projection.
|
267
|
+
# Note the ellps and datum set to WGS84.
|
268
|
+
factory = RGeo::Geographic.projected_factory(:projection_proj4 =>
|
269
|
+
'+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs')
|
270
|
+
|
271
|
+
# Create a point with the long/lat of Seattle
|
272
|
+
point = factory.point(-122.3, 47.6)
|
273
|
+
|
274
|
+
# Project the point to mercator projection coordinates.
|
275
|
+
# The coordinates are in "meters" at the equator.
|
276
|
+
point.projection.as_text # => "POINT (-13614373.724017357 6008996.432357133)"
|
277
|
+
|
278
|
+
=== 4.5. Spatial Reference Systems and the EPSG Dataset
|
279
|
+
|
280
|
+
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 does not yet implement this specification, but it is on the radar.
|
281
|
+
|
282
|
+
Finally, there also exists a de facto 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.
|
283
|
+
|
284
|
+
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.
|
285
|
+
|
286
|
+
== 5. Spatial Databases
|
287
|
+
|
288
|
+
Now that we have a basic understanding of geospatial data, we'll turn to the question of storing and querying for this data.
|
289
|
+
|
290
|
+
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 include conditions relating to the object itself in a query. Typical location-based applications may need to run queries such as "give me all the locations within one mile of this lat-long." This kind of capability is the domain of spatial databases.
|
291
|
+
|
292
|
+
=== 5.1. Spatial Queries and Indexes
|
293
|
+
|
294
|
+
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 restrictions. 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:
|
295
|
+
|
296
|
+
SELECT * FROM mytable WHERE ST_Distance(geom, ST_WKTToSQL("POINT(10 20)")) > 5;
|
297
|
+
|
298
|
+
Like all database queries, however, when there are a large number of rows, such queries can be slow because it has to scan and compare every row. This is especially true of geometric functions like the above, which can be numerically complex and slow to execute. To speed up queries, it is necessary to index your geometric columns.
|
299
|
+
|
300
|
+
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 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.
|
301
|
+
|
302
|
+
Spatial databases will handle the problem of indexing spatial data in different ways, but most are variants on an indexing algorithm known as an R-tree. I won't go into the details of how an R-tree works. 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.
|
303
|
+
|
304
|
+
=== 5.2. MySQL Spatial
|
305
|
+
|
306
|
+
{MySQL}[http://mysql.com/] maintains its dual reputation as probably the easiest of the open source relational databases to manage, and 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.
|
307
|
+
|
308
|
+
CREATE TABLE mytable(id INT NOT NULL PRIMARY KEY, latlon POINT) ENGINE=MyISAM;
|
309
|
+
CREATE SPATIAL INDEX idx_latlon ON mytable(latlon);
|
310
|
+
|
311
|
+
MySQL represents data internally using WKB slightly modified with 4 extra bytes at the beginning to store an object's SRID. You can interpret this data directly, or use provided functions to convert to and from WKB and WKT.
|
312
|
+
|
313
|
+
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 is not present, you will not be able to specify restrictions on your geometries beyond the type: you will not be able to restrict the SRID for a column, and columns will not be able to support Z and M coordinates. Because the latter is not present, MySQL cannot perform coordinate system transformations itself, nor provide you with the standard EPSG dataset for your own use.
|
314
|
+
|
315
|
+
In general, if you're using MySQL for the rest of your application, and you just need to add some simple geometric data, it's very easy to use MySQL spatial. Just make sure your table uses the MyISAM engine if you need to index the geometry.
|
316
|
+
|
317
|
+
=== 5.3. SpatiaLite
|
318
|
+
|
319
|
+
{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.
|
320
|
+
|
321
|
+
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 restrictions and maintain the spatial indexes.
|
322
|
+
|
323
|
+
CREATE TABLE mytable(id INTEGER PRIMARY KEY);
|
324
|
+
SELECT AddGeometryColumn('mytable', 'latlon', 4326, 'POINT', 'XY');
|
325
|
+
SELECT CreateSpatialIndex('mytable', 'latlon');
|
326
|
+
|
327
|
+
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:
|
328
|
+
|
329
|
+
SELECT * FROM mytable WHERE id IN
|
330
|
+
(SELECT pkid FROM idx_mytable_latlon WHERE
|
331
|
+
xmin > -123 AND xmax < -122 AND ymin > 47 AND ymax < 48);
|
332
|
+
|
333
|
+
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.
|
334
|
+
|
335
|
+
=== 5.4. PostGIS
|
336
|
+
|
337
|
+
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.
|
338
|
+
|
339
|
+
Like SpatiaLite, PostGIS is compiled and installed as a separate add-on library to Postgres. 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.
|
340
|
+
|
341
|
+
CREATE TABLE mytable(id INTEGER PRIMARY KEY);
|
342
|
+
SELECT AddGeometryColumn('mytable', 'latlon', 4326, 'POINTM', 3);
|
343
|
+
CREATE INDEX idx_mytable_latlon ON mytable USING GIST (latlon);
|
344
|
+
|
345
|
+
PostGIS uses as its internal format a variant on 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 common enough that they are supported by \RGeo's WKB and WKT parsers.
|
346
|
+
|
347
|
+
=== 5.5. \RGeo \ActiveRecord Integration
|
348
|
+
|
349
|
+
\RGeo provides extra support for web applications built on Ruby On Rails or similar frameworks, and 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 connection adapters for the databases, 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.
|
350
|
+
|
351
|
+
These are the names of these connection adapters:
|
352
|
+
|
353
|
+
* *mysqlspatial*: subclasses the mysql adapter and adds support for spatial types
|
354
|
+
* *mysql2spatial*: subclasses the mysql2 adapter and adds support for spatial types
|
355
|
+
* *spatialite*: subclasses the sqlite3 adapter and adds support for the SpatiaLite extension
|
356
|
+
* *postgis*: subclasses the postgresql adapter and adds support for the PostGIS extension
|
357
|
+
|
358
|
+
== 6. Location Service Integration
|
359
|
+
|
360
|
+
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.
|
361
|
+
|
362
|
+
=== 6.1. Shapefiles and Spatial Data Sets
|
363
|
+
|
364
|
+
{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 de facto standard despite its legacy and somewhat awkward characteristics. Today, many of the widely-available geospatial data sets are distributed as shapefiles.
|
365
|
+
|
366
|
+
The shapefile format is specified in an ESRI {whitepaper}[http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf]. It typically comprises three files: a geometric data 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". \RGeo provides a module, RGeo::Shapefile, that can read shapefiles and represent their contents as geometric data objects.
|
367
|
+
|
368
|
+
=== 6.2. GeoJSON and Location-Based Services
|
369
|
+
|
370
|
+
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 object format based on JSON. GeoJSON can encode any of the OGR geometry types, and includes facilities to attach 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 a RGeo::GeoJSON module that can read and write this format.
|
371
|
+
|
372
|
+
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.
|
373
|
+
|
374
|
+
== 7. Conclusion
|
375
|
+
|
376
|
+
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 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.
|
377
|
+
|
378
|
+
At the time of this writing, a number of open source tools and libraries are starting to mature. For Ruby and Rails developers, the \RGeo library, along with this emerging suite of tools, represents an important step in the support for writing the next generation of geospatial applications.
|
379
|
+
|
380
|
+
== History
|
381
|
+
|
382
|
+
* Version 0.1 / 5 Dec 2010: Initial draft.
|
data/Version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.22
|
@@ -398,7 +398,7 @@ module ActiveRecord
|
|
398
398
|
if str_.length == 0
|
399
399
|
nil
|
400
400
|
else
|
401
|
-
factory_ = ar_class_.rgeo_factory_generator.call(:srid => srid_, :
|
401
|
+
factory_ = ar_class_.rgeo_factory_generator.call(:srid => srid_, :has_z_coordinate => has_z_, :has_m_coordinate => has_m_, :geographic => geographic_)
|
402
402
|
marker_ = str_[0,1]
|
403
403
|
if marker_ == "\x00" || marker_ == "\x01"
|
404
404
|
::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true).parse(str_) rescue nil
|
data/lib/rgeo.rb
CHANGED
@@ -61,9 +61,9 @@
|
|
61
61
|
# Cartesian geometry that includes every operation defined in the SFS. It
|
62
62
|
# requires GEOS 3.2 or later.
|
63
63
|
#
|
64
|
-
# The RGeo::
|
64
|
+
# The RGeo::Geographic module contains spatial implementations that
|
65
65
|
# operate in latitude-longitude coordinates and are well-suited for
|
66
|
-
# geographic location based applications.
|
66
|
+
# geographic location based applications. Geographic implementations may
|
67
67
|
# also be linked to projections.
|
68
68
|
#
|
69
69
|
# The RGeo::WKRep module contains tools for reading and writing spatial
|
@@ -103,7 +103,7 @@
|
|
103
103
|
# require 'rgeo/feature'
|
104
104
|
# require 'rgeo/cartesian'
|
105
105
|
# require 'rgeo/coord_sys'
|
106
|
-
# require 'rgeo/
|
106
|
+
# require 'rgeo/geographic'
|
107
107
|
# require 'rgeo/geos'
|
108
108
|
# require 'rgeo/geo_json'
|
109
109
|
# require 'rgeo/shapefile'
|
@@ -120,7 +120,7 @@ module RGeo
|
|
120
120
|
autoload(:Error, 'rgeo/error')
|
121
121
|
autoload(:Feature, 'rgeo/feature')
|
122
122
|
autoload(:GeoJSON, 'rgeo/geo_json')
|
123
|
-
autoload(:
|
123
|
+
autoload(:Geographic, 'rgeo/geographic')
|
124
124
|
autoload(:Geos, 'rgeo/geos')
|
125
125
|
autoload(:ImplHelper, 'rgeo/impl_helper')
|
126
126
|
autoload(:Shapefile, 'rgeo/shapefile')
|