From bfd3e401e33100692d25e1b911c5ad7a58bae4bc Mon Sep 17 00:00:00 2001 From: Daniel Azuma Date: Mon, 25 Apr 2011 11:11:41 -0700 Subject: [PATCH] Fixes and updates related to WKT and WKB --- History.rdoc | 14 +++- Spatial_Programming_With_RGeo.rdoc | 16 ++-- Version | 2 +- ext/geos_c_impl/factory.c | 23 +++++- ext/geos_c_impl/factory.h | 2 + ext/geos_c_impl/geometry.c | 50 +++++++----- lib/rgeo/cartesian/factory.rb | 35 +++++++- lib/rgeo/cartesian/interface.rb | 24 +++++- lib/rgeo/feature/factory.rb | 2 +- lib/rgeo/feature/factory_generator.rb | 2 +- lib/rgeo/feature/types.rb | 6 +- lib/rgeo/geographic/factory.rb | 33 +++++++- lib/rgeo/geographic/interface.rb | 73 +++++++++++++++-- lib/rgeo/geographic/proj4_projector.rb | 9 ++- lib/rgeo/geos.rb | 2 +- lib/rgeo/geos/factory.rb | 73 ++++++++++++++++- lib/rgeo/geos/impl_additions.rb | 2 +- lib/rgeo/geos/interface.rb | 31 ++++++++ lib/rgeo/geos/zm_factory.rb | 35 +++++++- lib/rgeo/geos/zm_impl.rb | 6 +- .../impl_helper/basic_geometry_methods.rb | 4 +- lib/rgeo/wkrep/wkb_generator.rb | 25 +----- lib/rgeo/wkrep/wkb_parser.rb | 75 ++++++++---------- lib/rgeo/wkrep/wkt_generator.rb | 64 +++++---------- lib/rgeo/wkrep/wkt_parser.rb | 51 +++--------- test/geos/tc_parsing_unparsing.rb | 79 +++++++++++++++++++ test/wkrep/tc_wkb_parser.rb | 70 ++++++++-------- test/wkrep/tc_wkt_generator.rb | 50 ++++++------ test/wkrep/tc_wkt_parser.rb | 4 +- 29 files changed, 582 insertions(+), 280 deletions(-) create mode 100644 test/geos/tc_parsing_unparsing.rb diff --git a/History.rdoc b/History.rdoc index b11ea452..8c16cf09 100644 --- a/History.rdoc +++ b/History.rdoc @@ -1,15 +1,23 @@ +=== 0.2.9 / 2011-04-25 + +* 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. +* 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.) +* WKRep::WKTGenerator injects some more whitespace to make output more readable and more in line with the examples in the SFS. +* 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). +* The WKB parser auto-detects and interprets hex strings. + === 0.2.8 / 2011-04-11 * A .gemspec file is now available for gem building and bundler git integration. === 0.2.7 / 2011-04-09 -* 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. +* 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. * 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.) === 0.2.6 / 2011-03-31 -* Ring direction analysis crashed if any of the line segments were zero length. Fixed. (Reported by spara.) +* Ring direction analysis raised an exception if any of the line segments were zero length. Fixed. (Reported by spara.) === 0.2.5 / 2011-03-21 @@ -25,7 +33,7 @@ === 0.2.3 / 2010-12-19 -* The "simpler mercator" geographic type incorrectly reported EPSG 3857 instead of EPSG 3785 for the projection. Dyslexia fixed. +* The "simple mercator" geographic type incorrectly reported EPSG 3857 instead of EPSG 3785 for the projection. Dyslexia fixed. * Geographic types couldn't have their coord_sys set. Fixed. * 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. * There are now explicit methods you can call to obtain FactoryGenerator objects; you should not need to call method. diff --git a/Spatial_Programming_With_RGeo.rdoc b/Spatial_Programming_With_RGeo.rdoc index 254f0c8a..91cdc93c 100644 --- a/Spatial_Programming_With_RGeo.rdoc +++ b/Spatial_Programming_With_RGeo.rdoc @@ -242,12 +242,12 @@ Several size and distance calculations are available. You can compute the distan 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 as_text and as_binary methods to serialize the object into a data string, while \RGeo factories provide parse_wkt and parse_wkb methods to reconstruct geometric objects from their serialized form. p00 = factory.point(0, 0) - p00.as_text # returns "Point(0 0)" + p00.as_text # returns "Point (0.0 0.0)" p10 = factory.point(1, 0) line = factory.line(p00, p10) - line.as_text # returns "LineString(0 0, 1 0)" - p = factory.parse_wkt('POINT(3 4)') - p.x # returns 3 + line.as_text # returns "LineString (0.0 0.0, 1.0 0.0)" + p = factory.parse_wkt('POINT (3 4)') + p.x # returns 3.0 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. @@ -291,11 +291,11 @@ Does this matter in your application? The answer is, it depends: on what kind of 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. -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. +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. To completely specify a coordinate system, then, a number of parameters are involved. Below I briefly describe the major parameters and what they mean: -*Ellipsoid*: (Also called a *sphereoid*) An ellipsoid is an approximation of the shape of the earth, defined by the length of the semi-major axis, or the radius at the equator (measured in meters) and the inverse flattening 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. +*Ellipsoid*: (Also called a *sphereoid*) An ellipsoid is an approximation of the shape of the earth, defined by the length of the semi-major axis, or the radius at the equator (measured in meters) and the inverse flattening 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. *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. @@ -347,11 +347,11 @@ As we have seen, there exist a variety of ways to serialize geometric objects, n 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: - SELECT * FROM mytable WHERE ST_Distance(geom, ST_WKTToSQL("POINT(10 20)")) > 5; + SELECT * FROM mytable WHERE ST_Distance(geom, ST_WKTToSQL("POINT (10 20)")) > 5; 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. -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. +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)? 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. 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. diff --git a/Version b/Version index a45be462..1866a362 100644 --- a/Version +++ b/Version @@ -1 +1 @@ -0.2.8 +0.2.9 diff --git a/ext/geos_c_impl/factory.c b/ext/geos_c_impl/factory.c index 178b81c3..7193c9a5 100644 --- a/ext/geos_c_impl/factory.c +++ b/ext/geos_c_impl/factory.c @@ -99,6 +99,20 @@ static void destroy_geometry_func(RGeo_GeometryData* data) } +// Mark function for factory data. This marks the wkt and wkb generator +// handles so they don't get collected. + +static void mark_factory_func(RGeo_FactoryData* data) +{ + if (!NIL_P(data->wkrep_wkt_generator)) { + rb_gc_mark(data->wkrep_wkt_generator); + } + if (!NIL_P(data->wkrep_wkb_generator)) { + rb_gc_mark(data->wkrep_wkb_generator); + } +} + + // Mark function for geometry data. This marks the factory and klasses // held by the geometry so those don't get collected. @@ -193,7 +207,8 @@ static VALUE method_factory_parse_wkb(VALUE self, VALUE str) } -static VALUE cmethod_factory_create(VALUE klass, VALUE flags, VALUE srid, VALUE buffer_resolution) +static VALUE cmethod_factory_create(VALUE klass, VALUE flags, VALUE srid, VALUE buffer_resolution, + VALUE wkt_generator, VALUE wkb_generator) { VALUE result = Qnil; RGeo_FactoryData* data = ALLOC(RGeo_FactoryData); @@ -210,7 +225,9 @@ static VALUE cmethod_factory_create(VALUE klass, VALUE flags, VALUE srid, VALUE data->wkb_reader = NULL; data->wkt_writer = NULL; data->wkb_writer = NULL; - result = Data_Wrap_Struct(klass, NULL, destroy_factory_func, data); + data->wkrep_wkt_generator = wkt_generator; + data->wkrep_wkb_generator = wkb_generator; + result = Data_Wrap_Struct(klass, mark_factory_func, destroy_factory_func, data); } else { free(data); @@ -237,7 +254,7 @@ RGeo_Globals* rgeo_init_geos_factory() rb_define_method(geos_factory_class, "_srid", method_factory_srid, 0); rb_define_method(geos_factory_class, "_buffer_resolution", method_factory_buffer_resolution, 0); rb_define_method(geos_factory_class, "_flags", method_factory_flags, 0); - rb_define_module_function(geos_factory_class, "_create", cmethod_factory_create, 3); + rb_define_module_function(geos_factory_class, "_create", cmethod_factory_create, 5); // Wrap the globals in a Ruby object and store it off so we have access // to it later. Each factory instance will reference it internally. diff --git a/ext/geos_c_impl/factory.h b/ext/geos_c_impl/factory.h index c44d38c1..8af4d875 100644 --- a/ext/geos_c_impl/factory.h +++ b/ext/geos_c_impl/factory.h @@ -89,6 +89,8 @@ typedef struct { GEOSWKBReader* wkb_reader; GEOSWKTWriter* wkt_writer; GEOSWKBWriter* wkb_writer; + VALUE wkrep_wkt_generator; + VALUE wkrep_wkb_generator; int flags; int srid; int buffer_resolution; diff --git a/ext/geos_c_impl/geometry.c b/ext/geos_c_impl/geometry.c index 4c10cf49..a78383c1 100644 --- a/ext/geos_c_impl/geometry.c +++ b/ext/geos_c_impl/geometry.c @@ -209,16 +209,22 @@ static VALUE method_geometry_as_text(VALUE self) const GEOSGeometry* self_geom = self_data->geom; if (self_geom) { RGeo_FactoryData* factory_data = RGEO_FACTORY_DATA_PTR(self_data->factory); - GEOSWKTWriter* wkt_writer = factory_data->wkt_writer; - GEOSContextHandle_t geos_context = self_data->geos_context; - if (!wkt_writer) { - wkt_writer = GEOSWKTWriter_create_r(geos_context); - factory_data->wkt_writer = wkt_writer; + VALUE wkt_generator = factory_data->wkrep_wkt_generator; + if (!NIL_P(wkt_generator)) { + result = rb_funcall(wkt_generator, rb_intern("generate"), 1, self); } - char* str = GEOSWKTWriter_write_r(geos_context, wkt_writer, self_geom); - if (str) { - result = rb_str_new2(str); - GEOSFree_r(geos_context, str); + else { + GEOSWKTWriter* wkt_writer = factory_data->wkt_writer; + GEOSContextHandle_t geos_context = self_data->geos_context; + if (!wkt_writer) { + wkt_writer = GEOSWKTWriter_create_r(geos_context); + factory_data->wkt_writer = wkt_writer; + } + char* str = GEOSWKTWriter_write_r(geos_context, wkt_writer, self_geom); + if (str) { + result = rb_str_new2(str); + GEOSFree_r(geos_context, str); + } } } return result; @@ -232,17 +238,23 @@ static VALUE method_geometry_as_binary(VALUE self) const GEOSGeometry* self_geom = self_data->geom; if (self_geom) { RGeo_FactoryData* factory_data = RGEO_FACTORY_DATA_PTR(self_data->factory); - GEOSWKBWriter* wkb_writer = factory_data->wkb_writer; - GEOSContextHandle_t geos_context = self_data->geos_context; - if (!wkb_writer) { - wkb_writer = GEOSWKBWriter_create_r(geos_context); - factory_data->wkb_writer = wkb_writer; + VALUE wkb_generator = factory_data->wkrep_wkb_generator; + if (!NIL_P(wkb_generator)) { + result = rb_funcall(wkb_generator, rb_intern("generate"), 1, self); } - size_t size; - char* str = (char*)GEOSWKBWriter_write_r(geos_context, wkb_writer, self_geom, &size); - if (str) { - result = rb_str_new(str, size); - GEOSFree_r(geos_context, str); + else { + GEOSWKBWriter* wkb_writer = factory_data->wkb_writer; + GEOSContextHandle_t geos_context = self_data->geos_context; + if (!wkb_writer) { + wkb_writer = GEOSWKBWriter_create_r(geos_context); + factory_data->wkb_writer = wkb_writer; + } + size_t size; + char* str = (char*)GEOSWKBWriter_write_r(geos_context, wkb_writer, self_geom, &size); + if (str) { + result = rb_str_new(str, size); + GEOSFree_r(geos_context, str); + } } } return result; diff --git a/lib/rgeo/cartesian/factory.rb b/lib/rgeo/cartesian/factory.rb index 24aae0a3..b3e557f2 100644 --- a/lib/rgeo/cartesian/factory.rb +++ b/lib/rgeo/cartesian/factory.rb @@ -49,7 +49,7 @@ class Factory # Create a new simple cartesian factory. # - # See ::RGeo::Cartesian::simple_factory for a list of supported options. + # See ::RGeo::Cartesian.simple_factory for a list of supported options. def initialize(opts_={}) @has_z = opts_[:has_z_coordinate] ? true : false @@ -76,6 +76,35 @@ def initialize(opts_={}) end srid_ ||= @coord_sys.authority_code if @coord_sys @srid = srid_.to_i + + wkt_generator_ = opts_[:wkt_generator] + case wkt_generator_ + when ::Hash + @wkt_generator = WKRep::WKTGenerator.new(wkt_generator_) + else + @wkt_generator = WKRep::WKTGenerator.new(:convert_case => :upper) + end + wkb_generator_ = opts_[:wkb_generator] + case wkb_generator_ + when ::Hash + @wkb_generator = WKRep::WKBGenerator.new(wkb_generator_) + else + @wkb_generator = WKRep::WKBGenerator.new + end + wkt_parser_ = opts_[:wkt_parser] + case wkt_parser_ + when ::Hash + @wkt_parser = WKRep::WKTParser.new(self, wkt_parser_) + else + @wkt_parser = WKRep::WKTParser.new(self) + end + wkb_parser_ = opts_[:wkb_parser] + case wkb_parser_ + when ::Hash + @wkb_parser = WKRep::WKBParser.new(self, wkb_parser_) + else + @wkb_parser = WKRep::WKBParser.new(self) + end end @@ -113,14 +142,14 @@ def property(name_) # See ::RGeo::Feature::Factory#parse_wkt def parse_wkt(str_) - WKRep::WKTParser.new(self).parse(str_) + @wkt_parser.parse(str_) end # See ::RGeo::Feature::Factory#parse_wkb def parse_wkb(str_) - WKRep::WKBParser.new(self).parse(str_) + @wkb_parser.parse(str_) end diff --git a/lib/rgeo/cartesian/interface.rb b/lib/rgeo/cartesian/interface.rb index 4c8452a8..ed74e8a7 100644 --- a/lib/rgeo/cartesian/interface.rb +++ b/lib/rgeo/cartesian/interface.rb @@ -54,8 +54,9 @@ class << self # # The given options are passed to the factory's constructor. # What options are available depends on the particular - # implementation. See Geos::factory and Cartesian::simple_factory - # for details. Unsupported options are ignored. + # implementation. See RGeo::Geos.factory and + # RGeo::Cartesian.simple_factory for details. Unsupported options + # are ignored. def preferred_factory(opts_={}) if ::RGeo::Geos.supported? @@ -110,6 +111,25 @@ def preferred_factory(opts_={}) # Support a Z coordinate. Default is false. # [:has_m_coordinate] # Support an M coordinate. Default is false. + # [:wkt_parser] + # Configure the parser for WKT. The value is a hash of + # configuration parameters for WKRep::WKTParser.new. Default is + # the empty hash, indicating the default configuration for + # WKRep::WKTParser. + # [:wkb_parser] + # Configure the parser for WKB. The value is a hash of + # configuration parameters for WKRep::WKBParser.new. Default is + # the empty hash, indicating the default configuration for + # WKRep::WKBParser. + # [:wkt_generator] + # Configure the generator for WKT. The value is a hash of + # configuration parameters for WKRep::WKTGenerator.new. + # Default is {:convert_case => :upper}. + # [:wkb_generator] + # Configure the generator for WKT. The value is a hash of + # configuration parameters for WKRep::WKTGenerator.new. + # Default is the empty hash, indicating the default configuration + # for WKRep::WKBGenerator. def simple_factory(opts_={}) Cartesian::Factory.new(opts_) diff --git a/lib/rgeo/feature/factory.rb b/lib/rgeo/feature/factory.rb index 7d1874eb..b1c4ae1b 100644 --- a/lib/rgeo/feature/factory.rb +++ b/lib/rgeo/feature/factory.rb @@ -313,7 +313,7 @@ def coord_sys # # It should return either a casted result object, false, or nil. # A nil return value indicates that casting should be forced to - # fail (and RGeo::Feature::cast will return nil). + # fail (and RGeo::Feature.cast will return nil). # A false return value indicates that this method declines to # override the casting algorithm, and RGeo should use its default # algorithm to cast the object. Therefore, by default, you should diff --git a/lib/rgeo/feature/factory_generator.rb b/lib/rgeo/feature/factory_generator.rb index fb91c792..57d816b9 100644 --- a/lib/rgeo/feature/factory_generator.rb +++ b/lib/rgeo/feature/factory_generator.rb @@ -50,7 +50,7 @@ module Feature # factory generator. # # Many of the implementations provide a factory method for creating - # factories. For example, RGeo::Cartesian::preferred_factory can be + # factories. For example, RGeo::Cartesian.preferred_factory can be # called to create a factory using the preferred Cartesian # implementation. Thus, to get a corresponding factory generator, # you can use the method method. e.g. diff --git a/lib/rgeo/feature/types.rb b/lib/rgeo/feature/types.rb index 851c473f..bf0f3e8a 100644 --- a/lib/rgeo/feature/types.rb +++ b/lib/rgeo/feature/types.rb @@ -146,9 +146,9 @@ class << self # values to true. You can even combine separate arguments and hash # arguments. For example, the following three calls are equivalent: # - # Feature.cast(geom, :type => Feature::Point, :project => true) - # Feature.cast(geom, Feature::Point, :project => true) - # Feature.cast(geom, Feature::Point, :project) + # RGeo::Feature.cast(geom, :type => RGeo::Feature::Point, :project => true) + # RGeo::Feature.cast(geom, RGeo::Feature::Point, :project => true) + # RGeo::Feature.cast(geom, RGeo::Feature::Point, :project) # # RGeo provides a default casting algorithm. Individual feature # implementation factories may override this and customize the diff --git a/lib/rgeo/geographic/factory.rb b/lib/rgeo/geographic/factory.rb index 42dddf93..7ed76afe 100644 --- a/lib/rgeo/geographic/factory.rb +++ b/lib/rgeo/geographic/factory.rb @@ -74,6 +74,35 @@ def initialize(impl_prefix_, opts_={}) # :nodoc: if @coord_sys.kind_of?(::String) @coord_sys = CoordSys::CS.create_from_wkt(@coord_sys) rescue nil end + + wkt_generator_ = opts_[:wkt_generator] + case wkt_generator_ + when ::Hash + @wkt_generator = WKRep::WKTGenerator.new(wkt_generator_) + else + @wkt_generator = WKRep::WKTGenerator.new(:convert_case => :upper) + end + wkb_generator_ = opts_[:wkb_generator] + case wkb_generator_ + when ::Hash + @wkb_generator = WKRep::WKBGenerator.new(wkb_generator_) + else + @wkb_generator = WKRep::WKBGenerator.new + end + wkt_parser_ = opts_[:wkt_parser] + case wkt_parser_ + when ::Hash + @wkt_parser = WKRep::WKTParser.new(self, wkt_parser_) + else + @wkt_parser = WKRep::WKTParser.new(self) + end + wkb_parser_ = opts_[:wkb_parser] + case wkb_parser_ + when ::Hash + @wkb_parser = WKRep::WKBParser.new(self, wkb_parser_) + else + @wkb_parser = WKRep::WKBParser.new(self) + end end @@ -192,14 +221,14 @@ def property(name_) # See ::RGeo::Feature::Factory#parse_wkt def parse_wkt(str_) - WKRep::WKTParser.new(self).parse(str_) + @wkt_parser.parse(str_) end # See ::RGeo::Feature::Factory#parse_wkb def parse_wkb(str_) - WKRep::WKBParser.new(self).parse(str_) + @wkb_parser.parse(str_) end diff --git a/lib/rgeo/geographic/interface.rb b/lib/rgeo/geographic/interface.rb index c8f9cdc4..3a1b5c03 100644 --- a/lib/rgeo/geographic/interface.rb +++ b/lib/rgeo/geographic/interface.rb @@ -110,6 +110,25 @@ class << self # CoordSys::SRSDatabase::Interface. If both this and an SRID are # provided, they are used to look up the proj4 and coord_sys # objects from a spatial reference system database. + # [:wkt_parser] + # Configure the parser for WKT. The value is a hash of + # configuration parameters for WKRep::WKTParser.new. Default is + # the empty hash, indicating the default configuration for + # WKRep::WKTParser. + # [:wkb_parser] + # Configure the parser for WKB. The value is a hash of + # configuration parameters for WKRep::WKBParser.new. Default is + # the empty hash, indicating the default configuration for + # WKRep::WKBParser. + # [:wkt_generator] + # Configure the generator for WKT. The value is a hash of + # configuration parameters for WKRep::WKTGenerator.new. + # Default is {:convert_case => :upper}. + # [:wkb_generator] + # Configure the generator for WKT. The value is a hash of + # configuration parameters for WKRep::WKTGenerator.new. + # Default is the empty hash, indicating the default configuration + # for WKRep::WKBGenerator. def spherical_factory(opts_={}) proj4_ = opts_[:proj4] @@ -177,12 +196,31 @@ def spherical_factory(opts_={}) # Support a Z coordinate. Default is false. # [:has_m_coordinate] # Support an M coordinate. Default is false. + # [:wkt_parser] + # Configure the parser for WKT. The value is a hash of + # configuration parameters for WKRep::WKTParser.new. Default is + # the empty hash, indicating the default configuration for + # WKRep::WKTParser. + # [:wkb_parser] + # Configure the parser for WKB. The value is a hash of + # configuration parameters for WKRep::WKBParser.new. Default is + # the empty hash, indicating the default configuration for + # WKRep::WKBParser. + # [:wkt_generator] + # Configure the generator for WKT. The value is a hash of + # configuration parameters for WKRep::WKTGenerator.new. + # Default is {:convert_case => :upper}. + # [:wkb_generator] + # Configure the generator for WKT. The value is a hash of + # configuration parameters for WKRep::WKTGenerator.new. + # Default is the empty hash, indicating the default configuration + # for WKRep::WKBGenerator. # # You may also provide options understood by the underlying # projected Cartesian factory. For example, if GEOS is used for the # projected factory, you may also set the # :lenient_multi_polygon_assertions and - # :buffer_resolution options. See RGeo::Geos::factory for + # :buffer_resolution options. See RGeo::Geos.factory for # more details. def simple_mercator_factory(opts_={}) @@ -287,12 +325,31 @@ def simple_mercator_factory(opts_={}) # Note: this is ignored if a :projection_factory is # provided; in that case, the geographic factory's m-coordinate # availability will match the projection factory's setting. + # [:wkt_parser] + # Configure the parser for WKT. The value is a hash of + # configuration parameters for WKRep::WKTParser.new. Default is + # the empty hash, indicating the default configuration for + # WKRep::WKTParser. + # [:wkb_parser] + # Configure the parser for WKB. The value is a hash of + # configuration parameters for WKRep::WKBParser.new. Default is + # the empty hash, indicating the default configuration for + # WKRep::WKBParser. + # [:wkt_generator] + # Configure the generator for WKT. The value is a hash of + # configuration parameters for WKRep::WKTGenerator.new. + # Default is {:convert_case => :upper}. + # [:wkb_generator] + # Configure the generator for WKT. The value is a hash of + # configuration parameters for WKRep::WKTGenerator.new. + # Default is the empty hash, indicating the default configuration + # for WKRep::WKBGenerator. # # If a :projection_factory is _not_ provided, you may also # provide options for configuring the projected Cartesian factory. # For example, if GEOS is used for the projected factory, you may # also set the :lenient_multi_polygon_assertions and - # :buffer_resolution options. See RGeo::Geos::factory for + # :buffer_resolution options. See RGeo::Geos.factory for # more details. def projected_factory(opts_={}) @@ -333,7 +390,9 @@ def projected_factory(opts_={}) :coord_sys => coord_sys_, :srid => srid_.to_i, :has_z_coordinate => projection_factory_.property(:has_z_coordinate), - :has_m_coordinate => projection_factory_.property(:has_m_coordinate)) + :has_m_coordinate => projection_factory_.property(:has_m_coordinate), + :wkt_parser => opts_[:wkt_parser], :wkt_generator => opts_[:wkt_generator], + :wkb_parser => opts_[:wkb_parser], :wkb_generator => opts_[:wkb_generator]) projector_ = Geographic::Proj4Projector.create_from_existing_factory(factory_, projection_factory_) else @@ -388,7 +447,9 @@ def projected_factory(opts_={}) :coord_sys => coord_sys_, :srid => srid_.to_i, :has_z_coordinate => opts_[:has_z_coordinate], - :has_m_coordinate => opts_[:has_m_coordinate]) + :has_m_coordinate => opts_[:has_m_coordinate], + :wkt_parser => opts_[:wkt_parser], :wkt_generator => opts_[:wkt_generator], + :wkb_parser => opts_[:wkb_parser], :wkb_generator => opts_[:wkb_generator]) projector_ = Geographic::Proj4Projector.create_from_proj4(factory_, projection_proj4_, :srid => projection_srid_, @@ -396,7 +457,9 @@ def projected_factory(opts_={}) :buffer_resolution => opts_[:buffer_resolution], :lenient_multi_polygon_assertions => opts_[:lenient_multi_polygon_assertions], :has_z_coordinate => opts_[:has_z_coordinate], - :has_m_coordinate => opts_[:has_m_coordinate]) + :has_m_coordinate => opts_[:has_m_coordinate], + :wkt_parser => opts_[:wkt_parser], :wkt_generator => opts_[:wkt_generator], + :wkb_parser => opts_[:wkb_parser], :wkb_generator => opts_[:wkb_generator]) end factory_._set_projector(projector_) factory_ diff --git a/lib/rgeo/geographic/proj4_projector.rb b/lib/rgeo/geographic/proj4_projector.rb index 04c53ac0..3ee37249 100644 --- a/lib/rgeo/geographic/proj4_projector.rb +++ b/lib/rgeo/geographic/proj4_projector.rb @@ -82,7 +82,14 @@ def create_from_existing_factory(geography_factory_, projection_factory_) def create_from_proj4(geography_factory_, proj4_, opts_={}) - projection_factory_ = Cartesian.preferred_factory(:proj4 => proj4_, :coord_sys => opts_[:coord_sys], :srid => opts_[:srid], :buffer_resolution => opts_[:buffer_resolution], :lenient_multi_polygon_assertions => opts_[:lenient_multi_polygon_assertions], :has_z_coordinate => opts_[:has_z_coordinate], :has_m_coordinate => opts_[:has_m_coordinate]) + projection_factory_ = Cartesian.preferred_factory(:proj4 => proj4_, + :coord_sys => opts_[:coord_sys], :srid => opts_[:srid], + :buffer_resolution => opts_[:buffer_resolution], + :lenient_multi_polygon_assertions => opts_[:lenient_multi_polygon_assertions], + :has_z_coordinate => opts_[:has_z_coordinate], + :has_m_coordinate => opts_[:has_m_coordinate], + :wkt_parser => opts_[:wkt_parser], :wkt_generator => opts_[:wkt_generator], + :wkb_parser => opts_[:wkb_parser], :wkb_generator => opts_[:wkb_generator]) new(geography_factory_, projection_factory_) end diff --git a/lib/rgeo/geos.rb b/lib/rgeo/geos.rb index e99cde24..52a57b39 100644 --- a/lib/rgeo/geos.rb +++ b/lib/rgeo/geos.rb @@ -51,7 +51,7 @@ module RGeo # therefore not documented. # # To use the Geos implementation, first obtain a factory using the - # ::RGeo::Geos::factory method. You may then call any of the standard + # ::RGeo::Geos.factory method. You may then call any of the standard # factory methods on the resulting object. module Geos diff --git a/lib/rgeo/geos/factory.rb b/lib/rgeo/geos/factory.rb index ddca269a..ae0a5ed3 100644 --- a/lib/rgeo/geos/factory.rb +++ b/lib/rgeo/geos/factory.rb @@ -53,10 +53,13 @@ class << self # Create a new factory. Returns nil if the GEOS implementation is # not supported. # - # See ::RGeo::Geos::factory for a list of supported options. + # See ::RGeo::Geos.factory for a list of supported options. def create(opts_={}) + # Make sure GEOS is available return nil unless respond_to?(:_create) + + # Get flags to pass to the C extension flags_ = 0 flags_ |= 1 if opts_[:lenient_multi_polygon_assertions] flags_ |= 2 if opts_[:has_z_coordinate] @@ -64,8 +67,32 @@ def create(opts_={}) if flags_ & 6 == 6 raise Error::UnsupportedOperation, "GEOS cannot support both Z and M coordinates at the same time." end + + # Buffer resolution buffer_resolution_ = opts_[:buffer_resolution].to_i buffer_resolution_ = 1 if buffer_resolution_ < 1 + + # Interpret the generator options + wkt_generator_ = opts_[:wkt_generator] + case wkt_generator_ + when :geos + wkt_generator_ = nil + when ::Hash + wkt_generator_ = WKRep::WKTGenerator.new(wkt_generator_) + else + wkt_generator_ = WKRep::WKTGenerator.new(:convert_case => :upper) + end + wkb_generator_ = opts_[:wkb_generator] + case wkb_generator_ + when :geos + wkb_generator_ = nil + when ::Hash + wkb_generator_ = WKRep::WKBGenerator.new(wkb_generator_) + else + wkb_generator_ = WKRep::WKBGenerator.new + end + + # Coordinate system (srid, proj4, and coord_sys) srid_ = opts_[:srid] proj4_ = opts_[:proj4] if CoordSys::Proj4.supported? @@ -87,9 +114,39 @@ def create(opts_={}) end end srid_ ||= coord_sys_.authority_code if coord_sys_ - result_ = _create(flags_, srid_.to_i, buffer_resolution_) + + # Create the factory and set instance variables + result_ = _create(flags_, srid_.to_i, buffer_resolution_, wkt_generator_, wkb_generator_) + + # Interpret parser options + wkt_parser_ = opts_[:wkt_parser] + case wkt_parser_ + when :geos + wkt_parser_ = nil + when ::Hash + wkt_parser_ = WKRep::WKTParser.new(result_, wkt_parser_) + else + wkt_parser_ = WKRep::WKTParser.new(result_) + end + wkb_parser_ = opts_[:wkb_parser] + case wkb_parser_ + when :geos + wkb_parser_ = nil + when ::Hash + wkb_parser_ = WKRep::WKBParser.new(result_, wkb_parser_) + else + wkb_parser_ = WKRep::WKBParser.new(result_) + end + + # Set instance variables result_.instance_variable_set(:@proj4, proj4_) result_.instance_variable_set(:@coord_sys, coord_sys_) + result_.instance_variable_set(:@wkt_parser, wkt_parser_) + result_.instance_variable_set(:@wkb_parser, wkb_parser_) + result_.instance_variable_set(:@wkt_generator, wkt_generator_) + result_.instance_variable_set(:@wkb_generator, wkb_generator_) + + # Return the result result_ end alias_method :new, :create @@ -152,14 +209,22 @@ def property(name_) # See ::RGeo::Feature::Factory#parse_wkt def parse_wkt(str_) - _parse_wkt_impl(str_) + if @wkt_parser + @wkt_parser.parse(str_) + else + _parse_wkt_impl(str_) + end end # See ::RGeo::Feature::Factory#parse_wkb def parse_wkb(str_) - _parse_wkb_impl(str_) + if @wkb_parser + @wkb_parser.parse(str_) + else + _parse_wkb_impl(str_) + end end diff --git a/lib/rgeo/geos/impl_additions.rb b/lib/rgeo/geos/impl_additions.rb index 94f6c84d..073115fb 100644 --- a/lib/rgeo/geos/impl_additions.rb +++ b/lib/rgeo/geos/impl_additions.rb @@ -63,7 +63,7 @@ class Factory Feature::MultiPoint => MultiPointImpl, Feature::MultiLineString => MultiLineStringImpl, Feature::MultiPolygon => MultiPolygonImpl, - } + }.freeze end # :startdoc: diff --git a/lib/rgeo/geos/interface.rb b/lib/rgeo/geos/interface.rb index 064d2647..5cb27551 100644 --- a/lib/rgeo/geos/interface.rb +++ b/lib/rgeo/geos/interface.rb @@ -105,6 +105,37 @@ def is_geos?(object_) # Support z_coordinate. Default is false. # [:has_m_coordinate] # Support m_coordinate. Default is false. + # [:wkt_parser] + # Configure the parser for WKT. You may either pass a hash of + # configuration parameters for WKRep::WKTParser.new, or the + # special value :geos, indicating to use the native + # GEOS parser. Default is the empty hash, indicating the default + # configuration for WKRep::WKTParser. + # Note that the special :geos value is not supported for + # ZM factories, since GEOS currently can't handle ZM natively. + # [:wkb_parser] + # Configure the parser for WKB. You may either pass a hash of + # configuration parameters for WKRep::WKBParser.new, or the + # special value :geos, indicating to use the native + # GEOS parser. Default is the empty hash, indicating the default + # configuration for WKRep::WKBParser. + # Note that the special :geos value is not supported for + # ZM factories, since GEOS currently can't handle ZM natively. + # [:wkt_generator] + # Configure the generator for WKT. You may either pass a hash of + # configuration parameters for WKRep::WKTGenerator.new, or the + # special value :geos, indicating to use the native + # GEOS generator. Default is {:convert_case => :upper}. + # Note that the special :geos value is not supported for + # ZM factories, since GEOS currently can't handle ZM natively. + # [:wkb_generator] + # Configure the generator for WKB. You may either pass a hash of + # configuration parameters for WKRep::WKBGenerator.new, or the + # special value :geos, indicating to use the native + # GEOS generator. Default is the empty hash, indicating the + # default configuration for WKRep::WKBGenerator. + # Note that the special :geos value is not supported for + # ZM factories, since GEOS currently can't handle ZM natively. def factory(opts_={}) if supported? diff --git a/lib/rgeo/geos/zm_factory.rb b/lib/rgeo/geos/zm_factory.rb index b04cf9cc..c3834d5d 100644 --- a/lib/rgeo/geos/zm_factory.rb +++ b/lib/rgeo/geos/zm_factory.rb @@ -76,10 +76,41 @@ def initialize(opts_={}) # :nodoc: config_ = { :lenient_multi_polygon_assertions => opts_[:lenient_multi_polygon_assertions], :buffer_resolution => opts_[:buffer_resolution], + :wkt_generator => opts_[:wkt_generator], :wkt_parser => opts_[:wkt_parser], + :wkb_generator => opts_[:wkb_generator], :wkb_parser => opts_[:wkb_parser], :srid => srid_.to_i, :proj4 => proj4_, :coord_sys => coord_sys_, } @zfactory = Factory.create(config_.merge(:has_z_coordinate => true)) @mfactory = Factory.create(config_.merge(:has_m_coordinate => true)) + + wkt_generator_ = opts_[:wkt_generator] + case wkt_generator_ + when ::Hash + @wkt_generator = WKRep::WKTGenerator.new(wkt_generator_) + else + @wkt_generator = WKRep::WKTGenerator.new(:convert_case => :upper) + end + wkb_generator_ = opts_[:wkb_generator] + case wkb_generator_ + when ::Hash + @wkb_generator = WKRep::WKBGenerator.new(wkb_generator_) + else + @wkb_generator = WKRep::WKBGenerator.new + end + wkt_parser_ = opts_[:wkt_parser] + case wkt_parser_ + when ::Hash + @wkt_parser = WKRep::WKTParser.new(self, wkt_parser_) + else + @wkt_parser = WKRep::WKTParser.new(self) + end + wkb_parser_ = opts_[:wkb_parser] + case wkb_parser_ + when ::Hash + @wkb_parser = WKRep::WKBParser.new(self, wkb_parser_) + else + @wkb_parser = WKRep::WKBParser.new(self) + end end @@ -142,14 +173,14 @@ def property(name_) # See ::RGeo::Feature::Factory#parse_wkt def parse_wkt(str_) - WKRep::WKTParser.new(self).parse(str_) + @wkt_parser.parse(str_) end # See ::RGeo::Feature::Factory#parse_wkb def parse_wkb(str_) - WKRep::WKBParser.new(self).parse(str_) + @wkb_parser.parse(str_) end diff --git a/lib/rgeo/geos/zm_impl.rb b/lib/rgeo/geos/zm_impl.rb index 61c42759..000275d2 100644 --- a/lib/rgeo/geos/zm_impl.rb +++ b/lib/rgeo/geos/zm_impl.rb @@ -101,12 +101,12 @@ def envelope def as_text - WKRep::WKTGenerator.new.generate(self) + @factory.instance_variable_get(:@wkt_generator).generate(self) end def as_binary - WKRep::WKBGenerator.new.generate(self) + @factory.instance_variable_get(:@wkb_generator).generate(self) end @@ -415,7 +415,7 @@ class ZMGeometryImpl # :nodoc: Feature::MultiPoint => ZMGeometryCollectionImpl, Feature::MultiLineString => ZMMultiLineStringImpl, Feature::MultiPolygon => ZMMultiPolygonImpl, - } + }.freeze def self.create(factory_, zgeometry_, mgeometry_) diff --git a/lib/rgeo/impl_helper/basic_geometry_methods.rb b/lib/rgeo/impl_helper/basic_geometry_methods.rb index 76cb2f42..93450361 100644 --- a/lib/rgeo/impl_helper/basic_geometry_methods.rb +++ b/lib/rgeo/impl_helper/basic_geometry_methods.rb @@ -68,12 +68,12 @@ def factory def as_text - WKRep::WKTGenerator.new.generate(self) + @factory.instance_variable_get(:@wkt_generator).generate(self) end def as_binary - WKRep::WKBGenerator.new.generate(self) + @factory.instance_variable_get(:@wkb_generator).generate(self) end diff --git a/lib/rgeo/wkrep/wkb_generator.rb b/lib/rgeo/wkrep/wkb_generator.rb index b0dec06d..bc392929 100644 --- a/lib/rgeo/wkrep/wkb_generator.rb +++ b/lib/rgeo/wkrep/wkb_generator.rb @@ -85,7 +85,7 @@ class WKBGenerator Feature::MultiLineString => 5, Feature::MultiPolygon => 6, Feature::GeometryCollection => 7, - } + }.freeze # :startdoc: @@ -105,46 +105,23 @@ def type_format @type_format end - # Sets the format for type codes. See WKBGenerator for details. - def type_format=(value_) - @type_format = value_ - end - # Returns whether SRID is embedded. See WKBGenerator for details. def emit_ewkb_srid? @emit_ewkb_srid end - # Sets whether SRID is embedded. Available only when the type_format - # is :ewkb. See WKBGenerator for details. - def emit_ewkb_srid=(value_) - @emit_ewkb_srid = @type_format == :ewkb && value_ - end - # Returns whether output is converted to hex. # See WKBGenerator for details. def hex_format? @hex_format end - # Sets whether output is converted to hex. - # See WKBGenerator for details. - def hex_format=(value_) - @hex_format = value_ ? true : false - end - # Returns whether output is little-endian (NDR). # See WKBGenerator for details. def little_endian? @little_endian end - # Sets whether output is little-endian (NDR). - # See WKBGenerator for details. - def little_endian=(value_) - @little_endian = value_ ? true : false - end - # Generate and return the WKB format for the given geometry object, # according to the current settings. diff --git a/lib/rgeo/wkrep/wkb_parser.rb b/lib/rgeo/wkrep/wkb_parser.rb index 0d0a8b69..6f9f1ee5 100644 --- a/lib/rgeo/wkrep/wkb_parser.rb +++ b/lib/rgeo/wkrep/wkb_parser.rb @@ -81,7 +81,16 @@ class WKBParser # documentation for the options that can be passed. def initialize(factory_generator_=nil, opts_={}) - self.factory_generator = factory_generator_ + if factory_generator_.kind_of?(Feature::Factory::Instance) + @factory_generator = Feature::FactoryGenerator.single(factory_generator_) + @exact_factory = factory_generator_ + elsif factory_generator_.respond_to?(:call) + @factory_generator = factory_generator_ + @exact_factory = nil + else + @factory_generator = Cartesian.method(:preferred_factory) + @exact_factory = nil + end @support_ewkb = opts_[:support_ewkb] ? true : false @support_wkb12 = opts_[:support_wkb12] ? true : false @ignore_extra_bytes = opts_[:ignore_extra_bytes] ? true : false @@ -100,70 +109,41 @@ def exact_factory @exact_factory end - # Sets the factory_generator. See WKBParser for details. - def factory_generator=(value_) - if value_.kind_of?(Feature::Factory::Instance) - @factory_generator = Feature::FactoryGenerator.single(value_) - @exact_factory = value_ - elsif value_.respond_to?(:call) - @factory_generator = value_ - @exact_factory = nil - else - @factory_generator = Cartesian.method(:preferred_factory) - @exact_factory = nil - end - end - - # Sets the factory_generator to the given block. - # See WKBParser for details. - def to_generate_factory(&block_) - @factory_generator = block_ - end - # Returns true if this parser supports EWKB. # See WKBParser for details. def support_ewkb? @support_ewkb end - # Sets the the support_ewkb flag. See WKBParser for details. - def support_ewkb=(value_) - @support_ewkb = value_ ? true : false - end - # Returns true if this parser supports SFS 1.2 extensions. # See WKBParser for details. def support_wkb12? @support_wkb12 end - # Sets the the support_wkb12 flag. See WKBParser for details. - def support_wkb12=(value_) - @support_wkb12 = value_ ? true : false - end - # Returns true if this parser ignores extra bytes. # See WKBParser for details. def ignore_extra_bytes? @ignore_extra_bytes end - # Sets the the ignore_extra_bytes flag. See WKBParser for details. - def ignore_extra_bytes=(value_) - @ignore_extra_bytes = value_ ? true : false - end - - - # Parse the given hex string, and return a geometry object. - - def parse_hex(str_) - parse([str_].pack('H*')) + # Returns true if this parser can auto-detect hex. + # See WKBParser for details. + def auto_detect_hex? + @auto_detect_hex end - # Parse the given binary data, and return a geometry object. + # Parse the given binary data or hexadecimal string, and return a + # geometry object. + # + # The #parse_hex method is a synonym, present for historical + # reasons but deprecated. Use #parse instead. def parse(data_) + if data_[0,1] =~ /[0-9a-fA-F]/ + data_ = [data_].pack('H*') + end @cur_has_z = nil @cur_has_m = nil @cur_srid = nil @@ -183,10 +163,19 @@ def parse(data_) end obj_ end + alias_method :parse_hex, :parse def _parse_object(contained_) # :nodoc: - little_endian_ = _get_byte == 1 + endian_value_ = _get_byte + case endian_value_ + when 0 + little_endian_ = false + when 1 + little_endian_ = true + else + raise Error::ParseError, "Bad endian byte value: #{endian_value_}" + end type_code_ = _get_integer(little_endian_) has_z_ = false has_m_ = false diff --git a/lib/rgeo/wkrep/wkt_generator.rb b/lib/rgeo/wkrep/wkt_generator.rb index f5edf336..15e02afa 100644 --- a/lib/rgeo/wkrep/wkt_generator.rb +++ b/lib/rgeo/wkrep/wkt_generator.rb @@ -95,44 +95,22 @@ def tag_format @tag_format end - # Sets the format for type tags. See WKTGenerator for details. - def tag_format=(value_) - @tag_format = value_ - end - # Returns whether SRID is embedded. See WKTGenerator for details. def emit_ewkt_srid? @emit_ewkt_srid end - # Sets whether SRID is embedded. Available only when the tag_format - # is :ewkt. See WKTGenerator for details. - def emit_ewkt_srid=(value_) - @emit_ewkt_srid = @type_format == :ewkt && value_ - end - # Returns whether square brackets rather than parens are output. # See WKTGenerator for details. def square_brackets? @square_brackets end - # Sets whether square brackets rather than parens are output. - # See WKTGenerator for details. - def square_brackets=(value_) - @square_brackets = value_ ? true : false - end - # Returns the case for output. See WKTGenerator for details. def convert_case @convert_case end - # Sets the case for output. See WKTGenerator for details. - def convert_case=(value_) - @convert_case = value_ - end - # Generate and return the WKT format for the given geometry object, # according to the current settings. @@ -181,19 +159,19 @@ def _generate_feature(obj_, toplevel_=false) # :nodoc: end end if type_ == Feature::Point - tag_ + _generate_point(obj_) + "#{tag_} #{_generate_point(obj_)}" elsif type_.subtype_of?(Feature::LineString) - tag_ + _generate_line_string(obj_) + "#{tag_} #{_generate_line_string(obj_)}" elsif type_ == Feature::Polygon - tag_ + _generate_polygon(obj_) + "#{tag_} #{_generate_polygon(obj_)}" elsif type_ == Feature::GeometryCollection - tag_ + _generate_geometry_collection(obj_) + "#{tag_} #{_generate_geometry_collection(obj_)}" elsif type_ == Feature::MultiPoint - tag_ + _generate_multi_point(obj_) + "#{tag_} #{_generate_multi_point(obj_)}" elsif type_ == Feature::MultiLineString - tag_ + _generate_multi_line_string(obj_) + "#{tag_} #{_generate_multi_line_string(obj_)}" elsif type_ == Feature::MultiPolygon - tag_ + _generate_multi_polygon(obj_) + "#{tag_} #{_generate_multi_polygon(obj_)}" else raise Error::ParseError, "Unrecognized geometry type: #{type_}" end @@ -213,56 +191,56 @@ def _generate_point(obj_) # :nodoc: end - def _generate_line_string(obj_, contained_=false) # :nodoc: + def _generate_line_string(obj_) # :nodoc: if obj_.is_empty? - contained_ ? 'EMPTY' : ' EMPTY' + 'EMPTY' else - "#{@begin_bracket}#{obj_.points.map{ |p_| _generate_coords(p_) }.join(',')}#{@end_bracket}" + "#{@begin_bracket}#{obj_.points.map{ |p_| _generate_coords(p_) }.join(', ')}#{@end_bracket}" end end - def _generate_polygon(obj_, contained_=false) # :nodoc: + def _generate_polygon(obj_) # :nodoc: if obj_.is_empty? - contained_ ? 'EMPTY' : ' EMPTY' + 'EMPTY' else - "#{@begin_bracket}#{([_generate_line_string(obj_.exterior_ring, true)] + obj_.interior_rings.map{ |r_| _generate_line_string(r_, true) }).join(',')}#{@end_bracket}" + "#{@begin_bracket}#{([_generate_line_string(obj_.exterior_ring)] + obj_.interior_rings.map{ |r_| _generate_line_string(r_) }).join(', ')}#{@end_bracket}" end end def _generate_geometry_collection(obj_) # :nodoc: if obj_.is_empty? - ' EMPTY' + 'EMPTY' else - "#{@begin_bracket}#{obj_.map{ |f_| _generate_feature(f_) }.join(',')}#{@end_bracket}" + "#{@begin_bracket}#{obj_.map{ |f_| _generate_feature(f_) }.join(', ')}#{@end_bracket}" end end def _generate_multi_point(obj_) # :nodoc: if obj_.is_empty? - " EMPTY" + 'EMPTY' else - "#{@begin_bracket}#{obj_.map{ |f_| _generate_point(f_) }.join(',')}#{@end_bracket}" + "#{@begin_bracket}#{obj_.map{ |f_| _generate_point(f_) }.join(', ')}#{@end_bracket}" end end def _generate_multi_line_string(obj_) # :nodoc: if obj_.is_empty? - " EMPTY" + 'EMPTY' else - "#{@begin_bracket}#{obj_.map{ |f_| _generate_line_string(f_, true) }.join(',')}#{@end_bracket}" + "#{@begin_bracket}#{obj_.map{ |f_| _generate_line_string(f_) }.join(', ')}#{@end_bracket}" end end def _generate_multi_polygon(obj_) # :nodoc: if obj_.is_empty? - " EMPTY" + 'EMPTY' else - "#{@begin_bracket}#{obj_.map{ |f_| _generate_polygon(f_, true) }.join(',')}#{@end_bracket}" + "#{@begin_bracket}#{obj_.map{ |f_| _generate_polygon(f_) }.join(', ')}#{@end_bracket}" end end diff --git a/lib/rgeo/wkrep/wkt_parser.rb b/lib/rgeo/wkrep/wkt_parser.rb index eb2bd565..c56b9594 100644 --- a/lib/rgeo/wkrep/wkt_parser.rb +++ b/lib/rgeo/wkrep/wkt_parser.rb @@ -88,7 +88,16 @@ class WKTParser # documentation for the options that can be passed. def initialize(factory_generator_=nil, opts_={}) - self.factory_generator = factory_generator_ + if factory_generator_.kind_of?(Feature::Factory::Instance) + @factory_generator = Feature::FactoryGenerator.single(factory_generator_) + @exact_factory = factory_generator_ + elsif factory_generator_.respond_to?(:call) + @factory_generator = factory_generator_ + @exact_factory = nil + else + @factory_generator = Cartesian.method(:preferred_factory) + @exact_factory = nil + end @support_ewkt = opts_[:support_ewkt] ? true : false @support_wkt12 = opts_[:support_wkt12] ? true : false @strict_wkt11 = @support_ewkt || @support_wkt12 ? false : opts_[:strict_wkt11] ? true : false @@ -108,70 +117,30 @@ def exact_factory @exact_factory end - # Sets the factory_generator. See WKTParser for details. - def factory_generator=(value_) - if value_.kind_of?(Feature::Factory::Instance) - @factory_generator = Feature::FactoryGenerator.single(value_) - @exact_factory = value_ - elsif value_.respond_to?(:call) - @factory_generator = value_ - @exact_factory = nil - else - @factory_generator = Cartesian.method(:preferred_factory) - @exact_factory = nil - end - end - - # Sets the factory_generator to the given block. - # See WKTParser for details. - def to_generate_factory(&block_) - self.factory_generator = block_ - end - # Returns true if this parser supports EWKT. # See WKTParser for details. def support_ewkt? @support_ewkt end - # Sets the the support_ewkt flag. See WKTParser for details. - def support_ewkt=(value_) - @support_ewkt = value_ ? true : false - end - # Returns true if this parser supports SFS 1.2 extensions. # See WKTParser for details. def support_wkt12? @support_wkt12 end - # Sets the the support_wkt12 flag. See WKTParser for details. - def support_wkt12=(value_) - @support_wkt12 = value_ ? true : false - end - # Returns true if this parser strictly adheres to WKT 1.1. # See WKTParser for details. def strict_wkt11? @strict_wkt11 end - # Sets the the strict_wkt11 flag. See WKTParser for details. - def strict_wkt11=(value_) - @strict_wkt11 = value_ ? true : false - end - # Returns true if this parser ignores extra tokens. # See WKTParser for details. def ignore_extra_tokens? @ignore_extra_tokens end - # Sets the the ignore_extra_tokens flag. See WKTParser for details. - def ignore_extra_tokens=(value_) - @ignore_extra_tokens = value_ ? true : false - end - # Parse the given string, and return a geometry object. diff --git a/test/geos/tc_parsing_unparsing.rb b/test/geos/tc_parsing_unparsing.rb new file mode 100644 index 00000000..add73979 --- /dev/null +++ b/test/geos/tc_parsing_unparsing.rb @@ -0,0 +1,79 @@ +# ----------------------------------------------------------------------------- +# +# Tests for the GEOS point implementation +# +# ----------------------------------------------------------------------------- +# Copyright 2010 Daniel Azuma +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the copyright holder, nor the names of any other +# contributors to this software, may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- +; + + +require 'test/unit' +require 'rgeo' + + +module RGeo + module Tests # :nodoc: + module Geos # :nodoc: + + class TestParsingUnparsing < ::Test::Unit::TestCase # :nodoc: + + + def setup + @factory = ::RGeo::Geos.factory + end + + + def test_wkt_generator_default_floating_point + # Bug report GH-4 + factory_ = ::RGeo::Geos.factory + point_ = factory_.point(111.99, -40.37) + assert_equal('POINT (111.99 -40.37)', point_.as_text) + end + + + def test_wkt_generator_downcase + factory_ = ::RGeo::Geos.factory(:wkt_generator => {:convert_case => :lower}) + point_ = factory_.point(1, 1) + assert_equal('point (1.0 1.0)', point_.as_text) + end + + + def test_wkt_generator_geos + factory_ = ::RGeo::Geos.factory(:wkt_generator => :geos) + point_ = factory_.point(1, 1) + assert_equal('POINT (1.0000000000000000 1.0000000000000000)', point_.as_text) + end + + + end + + end + end +end if ::RGeo::Geos.supported? diff --git a/test/wkrep/tc_wkb_parser.rb b/test/wkrep/tc_wkb_parser.rb index 28ac3549..f9298558 100644 --- a/test/wkrep/tc_wkb_parser.rb +++ b/test/wkrep/tc_wkb_parser.rb @@ -47,7 +47,7 @@ class TestWKBParser < ::Test::Unit::TestCase # :nodoc: def test_point_2d_xdr parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('00000000013ff00000000000004000000000000000') + obj_ = parser_.parse('00000000013ff00000000000004000000000000000') assert_equal(::RGeo::Feature::Point, obj_.geometry_type) assert_equal(1, obj_.x) assert_equal(2, obj_.y) @@ -56,7 +56,7 @@ def test_point_2d_xdr def test_point_2d_ndr parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('0101000000000000000000f03f0000000000000040') + obj_ = parser_.parse('0101000000000000000000f03f0000000000000040') assert_equal(::RGeo::Feature::Point, obj_.geometry_type) assert_equal(1, obj_.x) assert_equal(2, obj_.y) @@ -66,7 +66,7 @@ def test_point_2d_ndr def test_point_with_ewkb_z factory_ = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true) parser_ = ::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true) - obj_ = parser_.parse_hex('00800000013ff000000000000040000000000000004008000000000000') + obj_ = parser_.parse('00800000013ff000000000000040000000000000004008000000000000') assert_equal(::RGeo::Feature::Point, obj_.geometry_type) assert_equal(3, obj_.z) assert_nil(obj_.m) @@ -76,7 +76,7 @@ def test_point_with_ewkb_z def test_point_with_ewkb_m factory_ = ::RGeo::Cartesian.preferred_factory(:has_m_coordinate => true) parser_ = ::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true) - obj_ = parser_.parse_hex('00400000013ff000000000000040000000000000004008000000000000') + obj_ = parser_.parse('00400000013ff000000000000040000000000000004008000000000000') assert_equal(::RGeo::Feature::Point, obj_.geometry_type) assert_equal(3, obj_.m) assert_nil(obj_.z) @@ -86,7 +86,7 @@ def test_point_with_ewkb_m def test_point_with_ewkb_zm factory_ = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true, :has_m_coordinate => true) parser_ = ::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true) - obj_ = parser_.parse_hex('00c00000013ff0000000000000400000000000000040080000000000004010000000000000') + obj_ = parser_.parse('00c00000013ff0000000000000400000000000000040080000000000004010000000000000') assert_equal(::RGeo::Feature::Point, obj_.geometry_type) assert_equal(3, obj_.z) assert_equal(4, obj_.m) @@ -96,7 +96,7 @@ def test_point_with_ewkb_zm def test_point_with_wkb12_z factory_ = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true) parser_ = ::RGeo::WKRep::WKBParser.new(factory_, :support_wkb12 => true) - obj_ = parser_.parse_hex('00000003e93ff000000000000040000000000000004008000000000000') + obj_ = parser_.parse('00000003e93ff000000000000040000000000000004008000000000000') assert_equal(::RGeo::Feature::Point, obj_.geometry_type) assert_equal(3, obj_.z) assert_nil(obj_.m) @@ -106,7 +106,7 @@ def test_point_with_wkb12_z def test_point_with_wkb12_m factory_ = ::RGeo::Cartesian.preferred_factory(:has_m_coordinate => true) parser_ = ::RGeo::WKRep::WKBParser.new(factory_, :support_wkb12 => true) - obj_ = parser_.parse_hex('00000007d13ff000000000000040000000000000004008000000000000') + obj_ = parser_.parse('00000007d13ff000000000000040000000000000004008000000000000') assert_equal(::RGeo::Feature::Point, obj_.geometry_type) assert_equal(3, obj_.m) assert_nil(obj_.z) @@ -116,7 +116,7 @@ def test_point_with_wkb12_m def test_point_with_wkb12_zm factory_ = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true, :has_m_coordinate => true) parser_ = ::RGeo::WKRep::WKBParser.new(factory_, :support_wkb12 => true) - obj_ = parser_.parse_hex('0000000bb93ff0000000000000400000000000000040080000000000004010000000000000') + obj_ = parser_.parse('0000000bb93ff0000000000000400000000000000040080000000000004010000000000000') assert_equal(::RGeo::Feature::Point, obj_.geometry_type) assert_equal(3, obj_.z) assert_equal(4, obj_.m) @@ -127,7 +127,7 @@ def test_point_with_wkb12_z_without_wkb12_support factory_ = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true) parser_ = ::RGeo::WKRep::WKBParser.new(factory_) assert_raise(::RGeo::Error::ParseError) do - obj_ = parser_.parse_hex('00000003e93ff000000000000040000000000000004008000000000000') + obj_ = parser_.parse('00000003e93ff000000000000040000000000000004008000000000000') end end @@ -136,18 +136,17 @@ def test_point_with_wkb12_z_without_enough_data factory_ = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true) parser_ = ::RGeo::WKRep::WKBParser.new(factory_, :support_wkb12 => true) assert_raise(::RGeo::Error::ParseError) do - obj_ = parser_.parse_hex('00000003e93ff00000000000004000000000000000') + obj_ = parser_.parse('00000003e93ff00000000000004000000000000000') end end def test_point_with_ewkb_z_and_srid - factory_ = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true) - parser_ = ::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true) - parser_.to_generate_factory do |config_| + factory_generator_ = ::Proc.new do |config_| ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true, :srid => config_[:srid]) end - obj_ = parser_.parse_hex('00a0000001000003e83ff000000000000040000000000000004008000000000000') + parser_ = ::RGeo::WKRep::WKBParser.new(factory_generator_, :support_ewkb => true) + obj_ = parser_.parse('00a0000001000003e83ff000000000000040000000000000004008000000000000') assert_equal(::RGeo::Feature::Point, obj_.geometry_type) assert_equal(3, obj_.z) assert_nil(obj_.m) @@ -157,7 +156,7 @@ def test_point_with_ewkb_z_and_srid def test_linestring_basic parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('0000000002000000033ff000000000000040000000000000004008000000000000401000000000000040140000000000004018000000000000') + obj_ = parser_.parse('0000000002000000033ff000000000000040000000000000004008000000000000401000000000000040140000000000004018000000000000') assert_equal(::RGeo::Feature::LineString, obj_.geometry_type) assert_equal(3, obj_.num_points) assert_equal(1, obj_.point_n(0).x) @@ -168,7 +167,7 @@ def test_linestring_basic def test_linestring_with_ewkb_z factory_ = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true) parser_ = ::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true) - obj_ = parser_.parse_hex('0080000002000000023ff000000000000040000000000000004008000000000000401000000000000040140000000000004018000000000000') + obj_ = parser_.parse('0080000002000000023ff000000000000040000000000000004008000000000000401000000000000040140000000000004018000000000000') assert_equal(::RGeo::Feature::LineString, obj_.geometry_type) assert_equal(2, obj_.num_points) assert_equal(1, obj_.point_n(0).x) @@ -177,12 +176,11 @@ def test_linestring_with_ewkb_z def test_linestring_with_ewkb_z_and_srid - factory_ = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true) - parser_ = ::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true) - parser_.to_generate_factory do |config_| + factory_generator_ = ::Proc.new do |config_| ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true, :srid => config_[:srid]) end - obj_ = parser_.parse_hex('00a0000002000003e8000000023ff000000000000040000000000000004008000000000000401000000000000040140000000000004018000000000000') + parser_ = ::RGeo::WKRep::WKBParser.new(factory_generator_, :support_ewkb => true) + obj_ = parser_.parse('00a0000002000003e8000000023ff000000000000040000000000000004008000000000000401000000000000040140000000000004018000000000000') assert_equal(::RGeo::Feature::LineString, obj_.geometry_type) assert_equal(2, obj_.num_points) assert_equal(1, obj_.point_n(0).x) @@ -194,7 +192,7 @@ def test_linestring_with_ewkb_z_and_srid def test_linestring_with_wkb12_z factory_ = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true) parser_ = ::RGeo::WKRep::WKBParser.new(factory_, :support_wkb12 => true) - obj_ = parser_.parse_hex('00000003ea000000023ff000000000000040000000000000004008000000000000401000000000000040140000000000004018000000000000') + obj_ = parser_.parse('00000003ea000000023ff000000000000040000000000000004008000000000000401000000000000040140000000000004018000000000000') assert_equal(::RGeo::Feature::LineString, obj_.geometry_type) assert_equal(2, obj_.num_points) assert_equal(1, obj_.point_n(0).x) @@ -204,7 +202,7 @@ def test_linestring_with_wkb12_z def test_linestring_empty parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('000000000200000000') + obj_ = parser_.parse('000000000200000000') assert_equal(::RGeo::Feature::LineString, obj_.geometry_type) assert_equal(0, obj_.num_points) end @@ -212,7 +210,7 @@ def test_linestring_empty def test_polygon_basic parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('000000000300000001000000043ff0000000000000400000000000000040080000000000004010000000000000401800000000000040140000000000003ff00000000000004000000000000000') + obj_ = parser_.parse('000000000300000001000000043ff0000000000000400000000000000040080000000000004010000000000000401800000000000040140000000000003ff00000000000004000000000000000') assert_equal(::RGeo::Feature::Polygon, obj_.geometry_type) assert_equal(4, obj_.exterior_ring.num_points) assert_equal(1, obj_.exterior_ring.point_n(0).x) @@ -222,7 +220,7 @@ def test_polygon_basic def test_polygon_empty parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('000000000300000000') + obj_ = parser_.parse('000000000300000000') assert_equal(::RGeo::Feature::Polygon, obj_.geometry_type) assert_equal(0, obj_.exterior_ring.num_points) end @@ -230,7 +228,7 @@ def test_polygon_empty def test_multipoint_basic parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('00000000040000000200000000013ff00000000000004000000000000000000000000140080000000000004010000000000000') + obj_ = parser_.parse('00000000040000000200000000013ff00000000000004000000000000000000000000140080000000000004010000000000000') assert_equal(::RGeo::Feature::MultiPoint, obj_.geometry_type) assert_equal(2, obj_.num_geometries) assert_equal(1, obj_[0].x) @@ -240,7 +238,7 @@ def test_multipoint_basic def test_multipoint_mixed_byte_order parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('0000000004000000020101000000000000000000f03f0000000000000040000000000140080000000000004010000000000000') + obj_ = parser_.parse('0000000004000000020101000000000000000000f03f0000000000000040000000000140080000000000004010000000000000') assert_equal(::RGeo::Feature::MultiPoint, obj_.geometry_type) assert_equal(2, obj_.num_geometries) assert_equal(1, obj_[0].x) @@ -251,7 +249,7 @@ def test_multipoint_mixed_byte_order def test_multipoint_with_ewkb_z factory_ = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true) parser_ = ::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true) - obj_ = parser_.parse_hex('00800000040000000200800000013ff0000000000000400000000000000040140000000000000080000001400800000000000040100000000000004018000000000000') + obj_ = parser_.parse('00800000040000000200800000013ff0000000000000400000000000000040140000000000000080000001400800000000000040100000000000004018000000000000') assert_equal(::RGeo::Feature::MultiPoint, obj_.geometry_type) assert_equal(2, obj_.num_geometries) assert_equal(1, obj_[0].x) @@ -266,14 +264,14 @@ def test_multipoint_ewkb_with_mixed_z factory_ = ::RGeo::Cartesian.preferred_factory(:has_z_coordinate => true) parser_ = ::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true) assert_raise(::RGeo::Error::ParseError) do - obj_ = parser_.parse_hex('00800000040000000200800000013ff000000000000040000000000000004014000000000000000000000140080000000000004010000000000000') + obj_ = parser_.parse('00800000040000000200800000013ff000000000000040000000000000004014000000000000000000000140080000000000004010000000000000') end end def test_multipoint_empty parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('000000000400000000') + obj_ = parser_.parse('000000000400000000') assert_equal(::RGeo::Feature::MultiPoint, obj_.geometry_type) assert_equal(0, obj_.num_geometries) end @@ -281,7 +279,7 @@ def test_multipoint_empty def test_multilinestring_basic parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('0000000005000000020000000002000000033ff000000000000040000000000000004008000000000000401000000000000040140000000000004018000000000000000000000200000002bff0000000000000c000000000000000c008000000000000c010000000000000') + obj_ = parser_.parse('0000000005000000020000000002000000033ff000000000000040000000000000004008000000000000401000000000000040140000000000004018000000000000000000000200000002bff0000000000000c000000000000000c008000000000000c010000000000000') assert_equal(::RGeo::Feature::MultiLineString, obj_.geometry_type) assert_equal(2, obj_.num_geometries) assert_equal(1, obj_[0].point_n(0).x) @@ -292,14 +290,14 @@ def test_multilinestring_basic def test_multilinestring_wrong_element_type parser_ = ::RGeo::WKRep::WKBParser.new assert_raise(::RGeo::Error::ParseError) do - obj_ = parser_.parse_hex('0000000005000000020000000002000000033ff00000000000004000000000000000400800000000000040100000000000004014000000000000401800000000000000000000013ff00000000000004000000000000000') + obj_ = parser_.parse('0000000005000000020000000002000000033ff00000000000004000000000000000400800000000000040100000000000004014000000000000401800000000000000000000013ff00000000000004000000000000000') end end def test_multilinestring_empty parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('000000000500000000') + obj_ = parser_.parse('000000000500000000') assert_equal(::RGeo::Feature::MultiLineString, obj_.geometry_type) assert_equal(0, obj_.num_geometries) end @@ -307,7 +305,7 @@ def test_multilinestring_empty def test_multipolygon_basic parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('000000000600000002000000000300000001000000043ff0000000000000400000000000000040080000000000004010000000000000401800000000000040140000000000003ff00000000000004000000000000000000000000300000000') + obj_ = parser_.parse('000000000600000002000000000300000001000000043ff0000000000000400000000000000040080000000000004010000000000000401800000000000040140000000000003ff00000000000004000000000000000000000000300000000') assert_equal(::RGeo::Feature::MultiPolygon, obj_.geometry_type) assert_equal(2, obj_.num_geometries) assert_equal(4, obj_[0].exterior_ring.num_points) @@ -319,7 +317,7 @@ def test_multipolygon_basic def test_multipolygon_empty parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('000000000600000000') + obj_ = parser_.parse('000000000600000000') assert_equal(::RGeo::Feature::MultiPolygon, obj_.geometry_type) assert_equal(0, obj_.num_geometries) end @@ -327,7 +325,7 @@ def test_multipolygon_empty def test_collection_basic parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('0000000007000000020000000002000000033ff0000000000000400000000000000040080000000000004010000000000000401400000000000040180000000000000000000001bff0000000000000c000000000000000') + obj_ = parser_.parse('0000000007000000020000000002000000033ff0000000000000400000000000000040080000000000004010000000000000401400000000000040180000000000000000000001bff0000000000000c000000000000000') assert_equal(::RGeo::Feature::GeometryCollection, obj_.geometry_type) assert_equal(2, obj_.num_geometries) assert_equal(::RGeo::Feature::LineString, obj_[0].geometry_type) @@ -340,7 +338,7 @@ def test_collection_basic def test_collection_empty parser_ = ::RGeo::WKRep::WKBParser.new - obj_ = parser_.parse_hex('000000000700000000') + obj_ = parser_.parse('000000000700000000') assert_equal(::RGeo::Feature::GeometryCollection, obj_.geometry_type) assert_equal(0, obj_.num_geometries) end diff --git a/test/wkrep/tc_wkt_generator.rb b/test/wkrep/tc_wkt_generator.rb index 2c01f42b..c7fb845a 100644 --- a/test/wkrep/tc_wkt_generator.rb +++ b/test/wkrep/tc_wkt_generator.rb @@ -56,119 +56,119 @@ def setup def test_point_2d generator_ = ::RGeo::WKRep::WKTGenerator.new obj_ = @factory.point(1, 2) - assert_equal('Point(1.0 2.0)', generator_.generate(obj_)) + assert_equal('Point (1.0 2.0)', generator_.generate(obj_)) end def test_point_z generator_ = ::RGeo::WKRep::WKTGenerator.new obj_ = @factoryz.point(1, 2, 3) - assert_equal('Point(1.0 2.0 3.0)', generator_.generate(obj_)) + assert_equal('Point (1.0 2.0 3.0)', generator_.generate(obj_)) end def test_point_z_wkt11strict generator_ = ::RGeo::WKRep::WKTGenerator.new(:tag_format => :wkt11_strict) obj_ = @factoryz.point(1, 2, 3) - assert_equal('Point(1.0 2.0)', generator_.generate(obj_)) + assert_equal('Point (1.0 2.0)', generator_.generate(obj_)) end def test_point_m generator_ = ::RGeo::WKRep::WKTGenerator.new obj_ = @factorym.point(1, 2, 3) - assert_equal('Point(1.0 2.0 3.0)', generator_.generate(obj_)) + assert_equal('Point (1.0 2.0 3.0)', generator_.generate(obj_)) end def test_point_zm generator_ = ::RGeo::WKRep::WKTGenerator.new obj_ = @factoryzm.point(1, 2, 3, 4) - assert_equal('Point(1.0 2.0 3.0 4.0)', generator_.generate(obj_)) + assert_equal('Point (1.0 2.0 3.0 4.0)', generator_.generate(obj_)) end def test_point_squarebrackets generator_ = ::RGeo::WKRep::WKTGenerator.new(:square_brackets => true) obj_ = @factory.point(1, 2) - assert_equal('Point[1.0 2.0]', generator_.generate(obj_)) + assert_equal('Point [1.0 2.0]', generator_.generate(obj_)) end def test_point_uppercase generator_ = ::RGeo::WKRep::WKTGenerator.new(:convert_case => :upper) obj_ = @factory.point(1, 2) - assert_equal('POINT(1.0 2.0)', generator_.generate(obj_)) + assert_equal('POINT (1.0 2.0)', generator_.generate(obj_)) end def test_point_lowercase generator_ = ::RGeo::WKRep::WKTGenerator.new(:convert_case => :lower) obj_ = @factory.point(1, 2) - assert_equal('point(1.0 2.0)', generator_.generate(obj_)) + assert_equal('point (1.0 2.0)', generator_.generate(obj_)) end def test_point_wkt12 generator_ = ::RGeo::WKRep::WKTGenerator.new(:tag_format => :wkt12) obj_ = @factory.point(1, 2) - assert_equal('Point(1.0 2.0)', generator_.generate(obj_)) + assert_equal('Point (1.0 2.0)', generator_.generate(obj_)) end def test_point_wkt12_z generator_ = ::RGeo::WKRep::WKTGenerator.new(:tag_format => :wkt12) obj_ = @factoryz.point(1, 2, 3) - assert_equal('Point Z(1.0 2.0 3.0)', generator_.generate(obj_)) + assert_equal('Point Z (1.0 2.0 3.0)', generator_.generate(obj_)) end def test_point_wkt12_m generator_ = ::RGeo::WKRep::WKTGenerator.new(:tag_format => :wkt12) obj_ = @factorym.point(1, 2, 3) - assert_equal('Point M(1.0 2.0 3.0)', generator_.generate(obj_)) + assert_equal('Point M (1.0 2.0 3.0)', generator_.generate(obj_)) end def test_point_wkt12_zm generator_ = ::RGeo::WKRep::WKTGenerator.new(:tag_format => :wkt12) obj_ = @factoryzm.point(1, 2, 3, 4) - assert_equal('Point ZM(1.0 2.0 3.0 4.0)', generator_.generate(obj_)) + assert_equal('Point ZM (1.0 2.0 3.0 4.0)', generator_.generate(obj_)) end def test_point_ewkt generator_ = ::RGeo::WKRep::WKTGenerator.new(:tag_format => :ewkt) obj_ = @factory.point(1, 2) - assert_equal('Point(1.0 2.0)', generator_.generate(obj_)) + assert_equal('Point (1.0 2.0)', generator_.generate(obj_)) end def test_point_ewkt_z generator_ = ::RGeo::WKRep::WKTGenerator.new(:tag_format => :ewkt) obj_ = @factoryz.point(1, 2, 3) - assert_equal('Point(1.0 2.0 3.0)', generator_.generate(obj_)) + assert_equal('Point (1.0 2.0 3.0)', generator_.generate(obj_)) end def test_point_ewkt_m generator_ = ::RGeo::WKRep::WKTGenerator.new(:tag_format => :ewkt) obj_ = @factorym.point(1, 2, 3) - assert_equal('PointM(1.0 2.0 3.0)', generator_.generate(obj_)) + assert_equal('PointM (1.0 2.0 3.0)', generator_.generate(obj_)) end def test_point_ewkt_zm generator_ = ::RGeo::WKRep::WKTGenerator.new(:tag_format => :ewkt) obj_ = @factoryzm.point(1, 2, 3, 4) - assert_equal('Point(1.0 2.0 3.0 4.0)', generator_.generate(obj_)) + assert_equal('Point (1.0 2.0 3.0 4.0)', generator_.generate(obj_)) end def test_point_ewkt_with_srid generator_ = ::RGeo::WKRep::WKTGenerator.new(:tag_format => :ewkt, :emit_ewkt_srid => true) obj_ = @factory.point(1, 2) - assert_equal('SRID=1000;Point(1.0 2.0)', generator_.generate(obj_)) + assert_equal('SRID=1000;Point (1.0 2.0)', generator_.generate(obj_)) end @@ -178,7 +178,7 @@ def test_linestring_basic p2_ = @factory.point(2, 2) p3_ = @factory.point(1, 1) obj_ = @factory.line_string([p1_, p2_, p3_]) - assert_equal('LineString(1.0 2.0,2.0 2.0,1.0 1.0)', generator_.generate(obj_)) + assert_equal('LineString (1.0 2.0, 2.0 2.0, 1.0 1.0)', generator_.generate(obj_)) end @@ -197,7 +197,7 @@ def test_polygon_basic p4_ = @factory.point(0, 10) ext_ = @factory.line_string([p1_, p2_, p3_, p4_, p1_]) obj_ = @factory.polygon(ext_) - assert_equal('Polygon((0.0 0.0,10.0 0.0,10.0 10.0,0.0 10.0,0.0 0.0))', generator_.generate(obj_)) + assert_equal('Polygon ((0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 10.0, 0.0 0.0))', generator_.generate(obj_)) end @@ -213,7 +213,7 @@ def test_polygon_with_hole ext_ = @factory.line_string([p1_, p2_, p3_, p4_, p1_]) int_ = @factory.line_string([p5_, p6_, p7_, p5_]) obj_ = @factory.polygon(ext_, [int_]) - assert_equal('Polygon((0.0 0.0,10.0 0.0,10.0 10.0,0.0 10.0,0.0 0.0),(1.0 1.0,2.0 2.0,3.0 1.0,1.0 1.0))', generator_.generate(obj_)) + assert_equal('Polygon ((0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 10.0, 0.0 0.0), (1.0 1.0, 2.0 2.0, 3.0 1.0, 1.0 1.0))', generator_.generate(obj_)) end @@ -230,7 +230,7 @@ def test_multipoint_basic p2_ = @factory.point(2, 2) p3_ = @factory.point(1, 1) obj_ = @factory.multi_point([p1_, p2_, p3_]) - assert_equal('MultiPoint((1.0 2.0),(2.0 2.0),(1.0 1.0))', generator_.generate(obj_)) + assert_equal('MultiPoint ((1.0 2.0), (2.0 2.0), (1.0 1.0))', generator_.generate(obj_)) end @@ -254,7 +254,7 @@ def test_multilinestring_basic ls2_ = @factory.line_string([p5_, p6_, p7_]) ls3_ = @factory.line_string([]) obj_ = @factory.multi_line_string([ls1_, ls2_, ls3_]) - assert_equal('MultiLineString((0.0 0.0,10.0 0.0,10.0 10.0,0.0 10.0,0.0 0.0),(1.0 1.0,2.0 2.0,3.0 1.0),EMPTY)', generator_.generate(obj_)) + assert_equal('MultiLineString ((0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 10.0, 0.0 0.0), (1.0 1.0, 2.0 2.0, 3.0 1.0), EMPTY)', generator_.generate(obj_)) end @@ -285,7 +285,7 @@ def test_multipolygon_basic poly2_ = @factory.polygon(@factory.line_string([])) poly3_ = @factory.polygon(ext3_) obj_ = @factory.multi_polygon([poly1_, poly2_, poly3_]) - assert_equal('MultiPolygon(((0.0 0.0,10.0 0.0,10.0 10.0,0.0 10.0,0.0 0.0),(1.0 1.0,2.0 2.0,3.0 1.0,1.0 1.0)),EMPTY,((20.0 20.0,30.0 20.0,30.0 30.0,20.0 30.0,20.0 20.0)))', generator_.generate(obj_)) + assert_equal('MultiPolygon (((0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 10.0, 0.0 0.0), (1.0 1.0, 2.0 2.0, 3.0 1.0, 1.0 1.0)), EMPTY, ((20.0 20.0, 30.0 20.0, 30.0 30.0, 20.0 30.0, 20.0 20.0)))', generator_.generate(obj_)) end @@ -318,7 +318,7 @@ def test_collection_basic obj1_ = @factory.multi_polygon([poly1_, poly2_, poly3_]) obj2_ = @factory.point(1, 2) obj_ = @factory.collection([obj1_, obj2_]) - assert_equal('GeometryCollection(MultiPolygon(((0.0 0.0,10.0 0.0,10.0 10.0,0.0 10.0,0.0 0.0),(1.0 1.0,2.0 2.0,3.0 1.0,1.0 1.0)),EMPTY,((20.0 20.0,30.0 20.0,30.0 30.0,20.0 30.0,20.0 20.0))),Point(1.0 2.0))', generator_.generate(obj_)) + assert_equal('GeometryCollection (MultiPolygon (((0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 10.0, 0.0 0.0), (1.0 1.0, 2.0 2.0, 3.0 1.0, 1.0 1.0)), EMPTY, ((20.0 20.0, 30.0 20.0, 30.0 30.0, 20.0 30.0, 20.0 20.0))), Point (1.0 2.0))', generator_.generate(obj_)) end @@ -344,7 +344,7 @@ def test_collection_wkt12_z obj1_ = @factoryz.multi_polygon([poly1_, poly2_, poly3_]) obj2_ = @factoryz.point(1, 2, 3) obj_ = @factoryz.collection([obj1_, obj2_]) - assert_equal('GeometryCollection Z(MultiPolygon Z(((0.0 0.0 0.0,10.0 0.0 0.0,10.0 10.0 0.0,0.0 10.0 0.0,0.0 0.0 0.0),(1.0 1.0 0.0,2.0 2.0 0.0,3.0 1.0 0.0,1.0 1.0 0.0)),EMPTY,((20.0 20.0 0.0,30.0 20.0 0.0,30.0 30.0 0.0,20.0 30.0 0.0,20.0 20.0 0.0))),Point Z(1.0 2.0 3.0))', generator_.generate(obj_)) + assert_equal('GeometryCollection Z (MultiPolygon Z (((0.0 0.0 0.0, 10.0 0.0 0.0, 10.0 10.0 0.0, 0.0 10.0 0.0, 0.0 0.0 0.0), (1.0 1.0 0.0, 2.0 2.0 0.0, 3.0 1.0 0.0, 1.0 1.0 0.0)), EMPTY, ((20.0 20.0 0.0, 30.0 20.0 0.0, 30.0 30.0 0.0, 20.0 30.0 0.0, 20.0 20.0 0.0))), Point Z (1.0 2.0 3.0))', generator_.generate(obj_)) end diff --git a/test/wkrep/tc_wkt_parser.rb b/test/wkrep/tc_wkt_parser.rb index bce3825b..be71d079 100644 --- a/test/wkrep/tc_wkt_parser.rb +++ b/test/wkrep/tc_wkt_parser.rb @@ -245,9 +245,7 @@ def test_point_strict_wkt11_with_z def test_point_non_ewkt_with_srid - factory_ = ::RGeo::Cartesian.preferred_factory - parser_ = ::RGeo::WKRep::WKTParser.new(factory_) - parser_.factory_generator = ::RGeo::Cartesian.method(:preferred_factory) + parser_ = ::RGeo::WKRep::WKTParser.new(::RGeo::Cartesian.method(:preferred_factory)) assert_raise(::RGeo::Error::ParseError) do obj_ = parser_.parse('SRID=1000;POINT(1 2)') end