rgeo 0.2.8 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
data/History.rdoc CHANGED
@@ -1,15 +1,23 @@
1
+ === 0.2.9 / 2011-04-25
2
+
3
+ * INCOMPATIBLE CHANGE: mutator methods for the configurations of the WKRep parsers and generators have been removed. Create a new parser/generator if you need to change behavior.
4
+ * POSSIBLE INCOMPATIBLE CHANGE: The GEOS implementation now uses WKRep (by default) instead of the native GEOS WKB/WKT parsers and generators. This is because of some issues with the GEOS 3.2.2 implementation: namely, that the GEOS WKT generator suffers from some floating-point roundoff issues due to its "fixed point" output, and that the GEOS WKT parser fails to recognize names not in all caps, in violation of the version 1.2 update of the SFS. (Thanks to sharpone74 for report GH-4.)
5
+ * WKRep::WKTGenerator injects some more whitespace to make output more readable and more in line with the examples in the SFS.
6
+ * It is now possible to configure the WKT/WKB parsers/generators for each of the implementations, by passing the configuration hash to the factory constructor. In addition, it is also possible to configure the GEOS factory to use the native GEOS WKT/WKB implementation instead of RGeo::WKRep (that is, to restore the behavior of RGeo <= 0.2.8).
7
+ * The WKB parser auto-detects and interprets hex strings.
8
+
1
9
  === 0.2.8 / 2011-04-11
2
10
 
3
11
  * A .gemspec file is now available for gem building and bundler git integration.
4
12
 
5
13
  === 0.2.7 / 2011-04-09
6
14
 
7
- * POSSIBLE INCOMPATIBLE CHANGE: GeometryCollection#geometry_n, Polygon#interior_ring_n, and LineString#point_n, in some implementations, allowed negative indexes (which counted backwards from the end of the collection as per Ruby arrays.) This was against the SFS interface, and so the behavior has been removed. However, GeometryCollection#[], because it is supposed to model Ruby arrays, now explicitly DOES allow negative indexes. This means GeometryCollection#[] is no longer exactly the same as GeometryCollection#geometry_n. These clarifications have also been made in the RDoc.
15
+ * POSSIBLE INCOMPATIBLE CHANGE: GeometryCollection#geometry_n, Polygon#interior_ring_n, and LineString#point_n, in some implementations, allowed negative indexes (which counted backwards from the end of the collection as per Ruby arrays). This was contrary to the SFS interface, and so the behavior has been removed. However, GeometryCollection#[], because it is supposed to model Ruby arrays, now explicitly DOES allow negative indexes. This means GeometryCollection#[] is no longer exactly the same as GeometryCollection#geometry_n. These clarifications have also been made in the RDoc.
8
16
  * The GEOS implementations of GeometryCollection#geometry_n and Polygon#interior_ring_n segfaulted when given an index out of bounds. Bounds Check Fail fixed. (Reported by sharpone74.)
9
17
 
10
18
  === 0.2.6 / 2011-03-31
11
19
 
12
- * Ring direction analysis crashed if any of the line segments were zero length. Fixed. (Reported by spara.)
20
+ * Ring direction analysis raised an exception if any of the line segments were zero length. Fixed. (Reported by spara.)
13
21
 
14
22
  === 0.2.5 / 2011-03-21
15
23
 
@@ -25,7 +33,7 @@
25
33
 
26
34
  === 0.2.3 / 2010-12-19
27
35
 
28
- * The "simpler mercator" geographic type incorrectly reported EPSG 3857 instead of EPSG 3785 for the projection. Dyslexia fixed.
36
+ * The "simple mercator" geographic type incorrectly reported EPSG 3857 instead of EPSG 3785 for the projection. Dyslexia fixed.
29
37
  * Geographic types couldn't have their coord_sys set. Fixed.
30
38
  * 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.
31
39
  * There are now explicit methods you can call to obtain FactoryGenerator objects; you should not need to call <tt>method</tt>.
@@ -242,12 +242,12 @@ Several size and distance calculations are available. You can compute the distan
242
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
243
 
244
244
  p00 = factory.point(0, 0)
245
- p00.as_text # returns "Point(0 0)"
245
+ p00.as_text # returns "Point (0.0 0.0)"
246
246
  p10 = factory.point(1, 0)
247
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
248
+ line.as_text # returns "LineString (0.0 0.0, 1.0 0.0)"
249
+ p = factory.parse_wkt('POINT (3 4)')
250
+ p.x # returns 3.0
251
251
 
252
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
253
 
@@ -291,11 +291,11 @@ Does this matter in your application? The answer is, it depends: on what kind of
291
291
 
292
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
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.
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, because a map projection is a function mapping latitude/longitude to flat coordinates, we need to specify _which_ latitude/longitude.
295
295
 
296
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
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.
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 will be followed by a "straight" line across the earth's surface.
299
299
 
300
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
301
 
@@ -347,11 +347,11 @@ As we have seen, there exist a variety of ways to serialize geometric objects, n
347
347
 
348
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
349
 
350
- SELECT * FROM mytable WHERE ST_Distance(geom, ST_WKTToSQL("POINT(10 20)")) > 5;
350
+ SELECT * FROM mytable WHERE ST_Distance(geom, ST_WKTToSQL("POINT (10 20)")) > 5;
351
351
 
352
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
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.
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 <tt>POINT (0 1)</tt> come before or after <tt>POINT (1 0)</tt>? And how do each of those compare with <tt>LINESTRING (0 1, 1 0)</tt>? Becase spatial data exists in two dimensions rather than one, and can span finite ranges in additional to infinitesimal points, the notion of a global ordering becomes ill-defined, and normal database indexes do not apply as well as we would like.
355
355
 
356
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
357
 
data/Version CHANGED
@@ -1 +1 @@
1
- 0.2.8
1
+ 0.2.9
@@ -99,6 +99,20 @@ static void destroy_geometry_func(RGeo_GeometryData* data)
99
99
  }
100
100
 
101
101
 
102
+ // Mark function for factory data. This marks the wkt and wkb generator
103
+ // handles so they don't get collected.
104
+
105
+ static void mark_factory_func(RGeo_FactoryData* data)
106
+ {
107
+ if (!NIL_P(data->wkrep_wkt_generator)) {
108
+ rb_gc_mark(data->wkrep_wkt_generator);
109
+ }
110
+ if (!NIL_P(data->wkrep_wkb_generator)) {
111
+ rb_gc_mark(data->wkrep_wkb_generator);
112
+ }
113
+ }
114
+
115
+
102
116
  // Mark function for geometry data. This marks the factory and klasses
103
117
  // held by the geometry so those don't get collected.
104
118
 
@@ -193,7 +207,8 @@ static VALUE method_factory_parse_wkb(VALUE self, VALUE str)
193
207
  }
194
208
 
195
209
 
196
- static VALUE cmethod_factory_create(VALUE klass, VALUE flags, VALUE srid, VALUE buffer_resolution)
210
+ static VALUE cmethod_factory_create(VALUE klass, VALUE flags, VALUE srid, VALUE buffer_resolution,
211
+ VALUE wkt_generator, VALUE wkb_generator)
197
212
  {
198
213
  VALUE result = Qnil;
199
214
  RGeo_FactoryData* data = ALLOC(RGeo_FactoryData);
@@ -210,7 +225,9 @@ static VALUE cmethod_factory_create(VALUE klass, VALUE flags, VALUE srid, VALUE
210
225
  data->wkb_reader = NULL;
211
226
  data->wkt_writer = NULL;
212
227
  data->wkb_writer = NULL;
213
- result = Data_Wrap_Struct(klass, NULL, destroy_factory_func, data);
228
+ data->wkrep_wkt_generator = wkt_generator;
229
+ data->wkrep_wkb_generator = wkb_generator;
230
+ result = Data_Wrap_Struct(klass, mark_factory_func, destroy_factory_func, data);
214
231
  }
215
232
  else {
216
233
  free(data);
@@ -237,7 +254,7 @@ RGeo_Globals* rgeo_init_geos_factory()
237
254
  rb_define_method(geos_factory_class, "_srid", method_factory_srid, 0);
238
255
  rb_define_method(geos_factory_class, "_buffer_resolution", method_factory_buffer_resolution, 0);
239
256
  rb_define_method(geos_factory_class, "_flags", method_factory_flags, 0);
240
- rb_define_module_function(geos_factory_class, "_create", cmethod_factory_create, 3);
257
+ rb_define_module_function(geos_factory_class, "_create", cmethod_factory_create, 5);
241
258
 
242
259
  // Wrap the globals in a Ruby object and store it off so we have access
243
260
  // to it later. Each factory instance will reference it internally.
@@ -89,6 +89,8 @@ typedef struct {
89
89
  GEOSWKBReader* wkb_reader;
90
90
  GEOSWKTWriter* wkt_writer;
91
91
  GEOSWKBWriter* wkb_writer;
92
+ VALUE wkrep_wkt_generator;
93
+ VALUE wkrep_wkb_generator;
92
94
  int flags;
93
95
  int srid;
94
96
  int buffer_resolution;
@@ -209,16 +209,22 @@ static VALUE method_geometry_as_text(VALUE self)
209
209
  const GEOSGeometry* self_geom = self_data->geom;
210
210
  if (self_geom) {
211
211
  RGeo_FactoryData* factory_data = RGEO_FACTORY_DATA_PTR(self_data->factory);
212
- GEOSWKTWriter* wkt_writer = factory_data->wkt_writer;
213
- GEOSContextHandle_t geos_context = self_data->geos_context;
214
- if (!wkt_writer) {
215
- wkt_writer = GEOSWKTWriter_create_r(geos_context);
216
- factory_data->wkt_writer = wkt_writer;
212
+ VALUE wkt_generator = factory_data->wkrep_wkt_generator;
213
+ if (!NIL_P(wkt_generator)) {
214
+ result = rb_funcall(wkt_generator, rb_intern("generate"), 1, self);
217
215
  }
218
- char* str = GEOSWKTWriter_write_r(geos_context, wkt_writer, self_geom);
219
- if (str) {
220
- result = rb_str_new2(str);
221
- GEOSFree_r(geos_context, str);
216
+ else {
217
+ GEOSWKTWriter* wkt_writer = factory_data->wkt_writer;
218
+ GEOSContextHandle_t geos_context = self_data->geos_context;
219
+ if (!wkt_writer) {
220
+ wkt_writer = GEOSWKTWriter_create_r(geos_context);
221
+ factory_data->wkt_writer = wkt_writer;
222
+ }
223
+ char* str = GEOSWKTWriter_write_r(geos_context, wkt_writer, self_geom);
224
+ if (str) {
225
+ result = rb_str_new2(str);
226
+ GEOSFree_r(geos_context, str);
227
+ }
222
228
  }
223
229
  }
224
230
  return result;
@@ -232,17 +238,23 @@ static VALUE method_geometry_as_binary(VALUE self)
232
238
  const GEOSGeometry* self_geom = self_data->geom;
233
239
  if (self_geom) {
234
240
  RGeo_FactoryData* factory_data = RGEO_FACTORY_DATA_PTR(self_data->factory);
235
- GEOSWKBWriter* wkb_writer = factory_data->wkb_writer;
236
- GEOSContextHandle_t geos_context = self_data->geos_context;
237
- if (!wkb_writer) {
238
- wkb_writer = GEOSWKBWriter_create_r(geos_context);
239
- factory_data->wkb_writer = wkb_writer;
241
+ VALUE wkb_generator = factory_data->wkrep_wkb_generator;
242
+ if (!NIL_P(wkb_generator)) {
243
+ result = rb_funcall(wkb_generator, rb_intern("generate"), 1, self);
240
244
  }
241
- size_t size;
242
- char* str = (char*)GEOSWKBWriter_write_r(geos_context, wkb_writer, self_geom, &size);
243
- if (str) {
244
- result = rb_str_new(str, size);
245
- GEOSFree_r(geos_context, str);
245
+ else {
246
+ GEOSWKBWriter* wkb_writer = factory_data->wkb_writer;
247
+ GEOSContextHandle_t geos_context = self_data->geos_context;
248
+ if (!wkb_writer) {
249
+ wkb_writer = GEOSWKBWriter_create_r(geos_context);
250
+ factory_data->wkb_writer = wkb_writer;
251
+ }
252
+ size_t size;
253
+ char* str = (char*)GEOSWKBWriter_write_r(geos_context, wkb_writer, self_geom, &size);
254
+ if (str) {
255
+ result = rb_str_new(str, size);
256
+ GEOSFree_r(geos_context, str);
257
+ }
246
258
  }
247
259
  }
248
260
  return result;
@@ -49,7 +49,7 @@ module RGeo
49
49
 
50
50
  # Create a new simple cartesian factory.
51
51
  #
52
- # See ::RGeo::Cartesian::simple_factory for a list of supported options.
52
+ # See ::RGeo::Cartesian.simple_factory for a list of supported options.
53
53
 
54
54
  def initialize(opts_={})
55
55
  @has_z = opts_[:has_z_coordinate] ? true : false
@@ -76,6 +76,35 @@ module RGeo
76
76
  end
77
77
  srid_ ||= @coord_sys.authority_code if @coord_sys
78
78
  @srid = srid_.to_i
79
+
80
+ wkt_generator_ = opts_[:wkt_generator]
81
+ case wkt_generator_
82
+ when ::Hash
83
+ @wkt_generator = WKRep::WKTGenerator.new(wkt_generator_)
84
+ else
85
+ @wkt_generator = WKRep::WKTGenerator.new(:convert_case => :upper)
86
+ end
87
+ wkb_generator_ = opts_[:wkb_generator]
88
+ case wkb_generator_
89
+ when ::Hash
90
+ @wkb_generator = WKRep::WKBGenerator.new(wkb_generator_)
91
+ else
92
+ @wkb_generator = WKRep::WKBGenerator.new
93
+ end
94
+ wkt_parser_ = opts_[:wkt_parser]
95
+ case wkt_parser_
96
+ when ::Hash
97
+ @wkt_parser = WKRep::WKTParser.new(self, wkt_parser_)
98
+ else
99
+ @wkt_parser = WKRep::WKTParser.new(self)
100
+ end
101
+ wkb_parser_ = opts_[:wkb_parser]
102
+ case wkb_parser_
103
+ when ::Hash
104
+ @wkb_parser = WKRep::WKBParser.new(self, wkb_parser_)
105
+ else
106
+ @wkb_parser = WKRep::WKBParser.new(self)
107
+ end
79
108
  end
80
109
 
81
110
 
@@ -113,14 +142,14 @@ module RGeo
113
142
  # See ::RGeo::Feature::Factory#parse_wkt
114
143
 
115
144
  def parse_wkt(str_)
116
- WKRep::WKTParser.new(self).parse(str_)
145
+ @wkt_parser.parse(str_)
117
146
  end
118
147
 
119
148
 
120
149
  # See ::RGeo::Feature::Factory#parse_wkb
121
150
 
122
151
  def parse_wkb(str_)
123
- WKRep::WKBParser.new(self).parse(str_)
152
+ @wkb_parser.parse(str_)
124
153
  end
125
154
 
126
155
 
@@ -54,8 +54,9 @@ module RGeo
54
54
  #
55
55
  # The given options are passed to the factory's constructor.
56
56
  # What options are available depends on the particular
57
- # implementation. See Geos::factory and Cartesian::simple_factory
58
- # for details. Unsupported options are ignored.
57
+ # implementation. See RGeo::Geos.factory and
58
+ # RGeo::Cartesian.simple_factory for details. Unsupported options
59
+ # are ignored.
59
60
 
60
61
  def preferred_factory(opts_={})
61
62
  if ::RGeo::Geos.supported?
@@ -110,6 +111,25 @@ module RGeo
110
111
  # Support a Z coordinate. Default is false.
111
112
  # [<tt>:has_m_coordinate</tt>]
112
113
  # Support an M coordinate. Default is false.
114
+ # [<tt>:wkt_parser</tt>]
115
+ # Configure the parser for WKT. The value is a hash of
116
+ # configuration parameters for WKRep::WKTParser.new. Default is
117
+ # the empty hash, indicating the default configuration for
118
+ # WKRep::WKTParser.
119
+ # [<tt>:wkb_parser</tt>]
120
+ # Configure the parser for WKB. The value is a hash of
121
+ # configuration parameters for WKRep::WKBParser.new. Default is
122
+ # the empty hash, indicating the default configuration for
123
+ # WKRep::WKBParser.
124
+ # [<tt>:wkt_generator</tt>]
125
+ # Configure the generator for WKT. The value is a hash of
126
+ # configuration parameters for WKRep::WKTGenerator.new.
127
+ # Default is <tt>{:convert_case => :upper}</tt>.
128
+ # [<tt>:wkb_generator</tt>]
129
+ # Configure the generator for WKT. The value is a hash of
130
+ # configuration parameters for WKRep::WKTGenerator.new.
131
+ # Default is the empty hash, indicating the default configuration
132
+ # for WKRep::WKBGenerator.
113
133
 
114
134
  def simple_factory(opts_={})
115
135
  Cartesian::Factory.new(opts_)
@@ -313,7 +313,7 @@ module RGeo
313
313
  #
314
314
  # It should return either a casted result object, false, or nil.
315
315
  # A nil return value indicates that casting should be forced to
316
- # fail (and RGeo::Feature::cast will return nil).
316
+ # fail (and RGeo::Feature.cast will return nil).
317
317
  # A false return value indicates that this method declines to
318
318
  # override the casting algorithm, and RGeo should use its default
319
319
  # algorithm to cast the object. Therefore, by default, you should
@@ -50,7 +50,7 @@ module RGeo
50
50
  # factory generator.
51
51
  #
52
52
  # Many of the implementations provide a factory method for creating
53
- # factories. For example, RGeo::Cartesian::preferred_factory can be
53
+ # factories. For example, RGeo::Cartesian.preferred_factory can be
54
54
  # called to create a factory using the preferred Cartesian
55
55
  # implementation. Thus, to get a corresponding factory generator,
56
56
  # you can use the <tt>method</tt> method. e.g.
@@ -146,9 +146,9 @@ module RGeo
146
146
  # values to true. You can even combine separate arguments and hash
147
147
  # arguments. For example, the following three calls are equivalent:
148
148
  #
149
- # Feature.cast(geom, :type => Feature::Point, :project => true)
150
- # Feature.cast(geom, Feature::Point, :project => true)
151
- # Feature.cast(geom, Feature::Point, :project)
149
+ # RGeo::Feature.cast(geom, :type => RGeo::Feature::Point, :project => true)
150
+ # RGeo::Feature.cast(geom, RGeo::Feature::Point, :project => true)
151
+ # RGeo::Feature.cast(geom, RGeo::Feature::Point, :project)
152
152
  #
153
153
  # RGeo provides a default casting algorithm. Individual feature
154
154
  # implementation factories may override this and customize the
@@ -74,6 +74,35 @@ module RGeo
74
74
  if @coord_sys.kind_of?(::String)
75
75
  @coord_sys = CoordSys::CS.create_from_wkt(@coord_sys) rescue nil
76
76
  end
77
+
78
+ wkt_generator_ = opts_[:wkt_generator]
79
+ case wkt_generator_
80
+ when ::Hash
81
+ @wkt_generator = WKRep::WKTGenerator.new(wkt_generator_)
82
+ else
83
+ @wkt_generator = WKRep::WKTGenerator.new(:convert_case => :upper)
84
+ end
85
+ wkb_generator_ = opts_[:wkb_generator]
86
+ case wkb_generator_
87
+ when ::Hash
88
+ @wkb_generator = WKRep::WKBGenerator.new(wkb_generator_)
89
+ else
90
+ @wkb_generator = WKRep::WKBGenerator.new
91
+ end
92
+ wkt_parser_ = opts_[:wkt_parser]
93
+ case wkt_parser_
94
+ when ::Hash
95
+ @wkt_parser = WKRep::WKTParser.new(self, wkt_parser_)
96
+ else
97
+ @wkt_parser = WKRep::WKTParser.new(self)
98
+ end
99
+ wkb_parser_ = opts_[:wkb_parser]
100
+ case wkb_parser_
101
+ when ::Hash
102
+ @wkb_parser = WKRep::WKBParser.new(self, wkb_parser_)
103
+ else
104
+ @wkb_parser = WKRep::WKBParser.new(self)
105
+ end
77
106
  end
78
107
 
79
108
 
@@ -192,14 +221,14 @@ module RGeo
192
221
  # See ::RGeo::Feature::Factory#parse_wkt
193
222
 
194
223
  def parse_wkt(str_)
195
- WKRep::WKTParser.new(self).parse(str_)
224
+ @wkt_parser.parse(str_)
196
225
  end
197
226
 
198
227
 
199
228
  # See ::RGeo::Feature::Factory#parse_wkb
200
229
 
201
230
  def parse_wkb(str_)
202
- WKRep::WKBParser.new(self).parse(str_)
231
+ @wkb_parser.parse(str_)
203
232
  end
204
233
 
205
234
 
@@ -110,6 +110,25 @@ module RGeo
110
110
  # CoordSys::SRSDatabase::Interface. If both this and an SRID are
111
111
  # provided, they are used to look up the proj4 and coord_sys
112
112
  # objects from a spatial reference system database.
113
+ # [<tt>:wkt_parser</tt>]
114
+ # Configure the parser for WKT. The value is a hash of
115
+ # configuration parameters for WKRep::WKTParser.new. Default is
116
+ # the empty hash, indicating the default configuration for
117
+ # WKRep::WKTParser.
118
+ # [<tt>:wkb_parser</tt>]
119
+ # Configure the parser for WKB. The value is a hash of
120
+ # configuration parameters for WKRep::WKBParser.new. Default is
121
+ # the empty hash, indicating the default configuration for
122
+ # WKRep::WKBParser.
123
+ # [<tt>:wkt_generator</tt>]
124
+ # Configure the generator for WKT. The value is a hash of
125
+ # configuration parameters for WKRep::WKTGenerator.new.
126
+ # Default is <tt>{:convert_case => :upper}</tt>.
127
+ # [<tt>:wkb_generator</tt>]
128
+ # Configure the generator for WKT. The value is a hash of
129
+ # configuration parameters for WKRep::WKTGenerator.new.
130
+ # Default is the empty hash, indicating the default configuration
131
+ # for WKRep::WKBGenerator.
113
132
 
114
133
  def spherical_factory(opts_={})
115
134
  proj4_ = opts_[:proj4]
@@ -177,12 +196,31 @@ module RGeo
177
196
  # Support a Z coordinate. Default is false.
178
197
  # [<tt>:has_m_coordinate</tt>]
179
198
  # Support an M coordinate. Default is false.
199
+ # [<tt>:wkt_parser</tt>]
200
+ # Configure the parser for WKT. The value is a hash of
201
+ # configuration parameters for WKRep::WKTParser.new. Default is
202
+ # the empty hash, indicating the default configuration for
203
+ # WKRep::WKTParser.
204
+ # [<tt>:wkb_parser</tt>]
205
+ # Configure the parser for WKB. The value is a hash of
206
+ # configuration parameters for WKRep::WKBParser.new. Default is
207
+ # the empty hash, indicating the default configuration for
208
+ # WKRep::WKBParser.
209
+ # [<tt>:wkt_generator</tt>]
210
+ # Configure the generator for WKT. The value is a hash of
211
+ # configuration parameters for WKRep::WKTGenerator.new.
212
+ # Default is <tt>{:convert_case => :upper}</tt>.
213
+ # [<tt>:wkb_generator</tt>]
214
+ # Configure the generator for WKT. The value is a hash of
215
+ # configuration parameters for WKRep::WKTGenerator.new.
216
+ # Default is the empty hash, indicating the default configuration
217
+ # for WKRep::WKBGenerator.
180
218
  #
181
219
  # You may also provide options understood by the underlying
182
220
  # projected Cartesian factory. For example, if GEOS is used for the
183
221
  # projected factory, you may also set the
184
222
  # <tt>:lenient_multi_polygon_assertions</tt> and
185
- # <tt>:buffer_resolution</tt> options. See RGeo::Geos::factory for
223
+ # <tt>:buffer_resolution</tt> options. See RGeo::Geos.factory for
186
224
  # more details.
187
225
 
188
226
  def simple_mercator_factory(opts_={})
@@ -287,12 +325,31 @@ module RGeo
287
325
  # Note: this is ignored if a <tt>:projection_factory</tt> is
288
326
  # provided; in that case, the geographic factory's m-coordinate
289
327
  # availability will match the projection factory's setting.
328
+ # [<tt>:wkt_parser</tt>]
329
+ # Configure the parser for WKT. The value is a hash of
330
+ # configuration parameters for WKRep::WKTParser.new. Default is
331
+ # the empty hash, indicating the default configuration for
332
+ # WKRep::WKTParser.
333
+ # [<tt>:wkb_parser</tt>]
334
+ # Configure the parser for WKB. The value is a hash of
335
+ # configuration parameters for WKRep::WKBParser.new. Default is
336
+ # the empty hash, indicating the default configuration for
337
+ # WKRep::WKBParser.
338
+ # [<tt>:wkt_generator</tt>]
339
+ # Configure the generator for WKT. The value is a hash of
340
+ # configuration parameters for WKRep::WKTGenerator.new.
341
+ # Default is <tt>{:convert_case => :upper}</tt>.
342
+ # [<tt>:wkb_generator</tt>]
343
+ # Configure the generator for WKT. The value is a hash of
344
+ # configuration parameters for WKRep::WKTGenerator.new.
345
+ # Default is the empty hash, indicating the default configuration
346
+ # for WKRep::WKBGenerator.
290
347
  #
291
348
  # If a <tt>:projection_factory</tt> is _not_ provided, you may also
292
349
  # provide options for configuring the projected Cartesian factory.
293
350
  # For example, if GEOS is used for the projected factory, you may
294
351
  # also set the <tt>:lenient_multi_polygon_assertions</tt> and
295
- # <tt>:buffer_resolution</tt> options. See RGeo::Geos::factory for
352
+ # <tt>:buffer_resolution</tt> options. See RGeo::Geos.factory for
296
353
  # more details.
297
354
 
298
355
  def projected_factory(opts_={})
@@ -333,7 +390,9 @@ module RGeo
333
390
  :coord_sys => coord_sys_,
334
391
  :srid => srid_.to_i,
335
392
  :has_z_coordinate => projection_factory_.property(:has_z_coordinate),
336
- :has_m_coordinate => projection_factory_.property(:has_m_coordinate))
393
+ :has_m_coordinate => projection_factory_.property(:has_m_coordinate),
394
+ :wkt_parser => opts_[:wkt_parser], :wkt_generator => opts_[:wkt_generator],
395
+ :wkb_parser => opts_[:wkb_parser], :wkb_generator => opts_[:wkb_generator])
337
396
  projector_ = Geographic::Proj4Projector.create_from_existing_factory(factory_,
338
397
  projection_factory_)
339
398
  else
@@ -388,7 +447,9 @@ module RGeo
388
447
  :coord_sys => coord_sys_,
389
448
  :srid => srid_.to_i,
390
449
  :has_z_coordinate => opts_[:has_z_coordinate],
391
- :has_m_coordinate => opts_[:has_m_coordinate])
450
+ :has_m_coordinate => opts_[:has_m_coordinate],
451
+ :wkt_parser => opts_[:wkt_parser], :wkt_generator => opts_[:wkt_generator],
452
+ :wkb_parser => opts_[:wkb_parser], :wkb_generator => opts_[:wkb_generator])
392
453
  projector_ = Geographic::Proj4Projector.create_from_proj4(factory_,
393
454
  projection_proj4_,
394
455
  :srid => projection_srid_,
@@ -396,7 +457,9 @@ module RGeo
396
457
  :buffer_resolution => opts_[:buffer_resolution],
397
458
  :lenient_multi_polygon_assertions => opts_[:lenient_multi_polygon_assertions],
398
459
  :has_z_coordinate => opts_[:has_z_coordinate],
399
- :has_m_coordinate => opts_[:has_m_coordinate])
460
+ :has_m_coordinate => opts_[:has_m_coordinate],
461
+ :wkt_parser => opts_[:wkt_parser], :wkt_generator => opts_[:wkt_generator],
462
+ :wkb_parser => opts_[:wkb_parser], :wkb_generator => opts_[:wkb_generator])
400
463
  end
401
464
  factory_._set_projector(projector_)
402
465
  factory_