From e5de86ec30cecacf6469845248e0d7191549259a Mon Sep 17 00:00:00 2001 From: Teck Wan <36706647+teckwan@users.noreply.github.com> Date: Thu, 16 Sep 2021 11:21:42 +0200 Subject: [PATCH] Add conversion methods to ease usage (#56) --- History.md | 1 + lib/rgeo/geo_json.rb | 1 + lib/rgeo/geo_json/conversion_methods.rb | 50 ++++++++++++++++++++++++ lib/rgeo/geo_json/entities.rb | 3 ++ test/basic_test.rb | 45 +++++++++++++++++++++ test/common/conversion_test.rb | 29 ++++++++++++++ test/geos_capi/conversion_test.rb | 13 ++++++ test/geos_ffi/conversion_test.rb | 13 ++++++ test/simple_cartesian/conversion_test.rb | 13 ++++++ test/simple_mercator/conversion_test.rb | 13 ++++++ 10 files changed, 181 insertions(+) create mode 100644 lib/rgeo/geo_json/conversion_methods.rb create mode 100644 test/common/conversion_test.rb create mode 100644 test/geos_capi/conversion_test.rb create mode 100644 test/geos_ffi/conversion_test.rb create mode 100644 test/simple_cartesian/conversion_test.rb create mode 100644 test/simple_mercator/conversion_test.rb diff --git a/History.md b/History.md index 3aa8a6c..5a947ad 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ * Delegation to inner geometry for a feature (#53) * MultiJson rather than JSON (#46) +* Conversion methods for better integration with core (#56) ### 2.1.1 / 2018-11-27 diff --git a/lib/rgeo/geo_json.rb b/lib/rgeo/geo_json.rb index aca6a12..5c398ad 100644 --- a/lib/rgeo/geo_json.rb +++ b/lib/rgeo/geo_json.rb @@ -2,6 +2,7 @@ require "rgeo" require_relative "geo_json/version" +require_relative "geo_json/conversion_methods" require_relative "geo_json/entities" require_relative "geo_json/coder" require_relative "geo_json/interface" diff --git a/lib/rgeo/geo_json/conversion_methods.rb b/lib/rgeo/geo_json/conversion_methods.rb new file mode 100644 index 0000000..d669774 --- /dev/null +++ b/lib/rgeo/geo_json/conversion_methods.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module RGeo + # This module serves to provide handy methods when using GeoJSON. The methods + # provided eases the passage between entities and GeoJSON String/Hash. + module GeoJSON::ConversionMethods + # Convert a geometry to a GeoJSON Hash + def as_geojson + GeoJSON.encode(self) + end + alias as_json as_geojson + + # Convert a geometry to a GeoJSON String + def to_geojson + ::MultiJson.dump(as_geojson) + end + alias to_json to_geojson + end + + # These convenience methods are added directly into the module rather than + # including the module above into the Feature::Instance module which is in + # every geometry implementation. This is due to a behavior in ruby versions + # <3.0.2 where dynamically included modules will not be included automatically + # in the ancestor tree. + # See https://bugs.ruby-lang.org/issues/9573 for more information. + module Feature + module Instance + # Convert a geometry to a GeoJSON Hash + def as_geojson + GeoJSON.encode(self) + end + alias as_json as_geojson + + # Convert a geometry to a GeoJSON String + def to_geojson + ::MultiJson.dump(as_geojson) + end + alias to_json to_geojson + end + + module Factory::Instance + # Parses a GeoJSON String/Hash, or an IO object from which + # the JSON string is read and returns the corresponding + # feature. Returns nil if unable to parse. + def parse_geojson(input) + GeoJSON.decode(input, geo_factory: self) + end + end + end +end diff --git a/lib/rgeo/geo_json/entities.rb b/lib/rgeo/geo_json/entities.rb index 6e17292..9a2e1f4 100644 --- a/lib/rgeo/geo_json/entities.rb +++ b/lib/rgeo/geo_json/entities.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative "collection_methods" +require_relative "conversion_methods" module RGeo module CastOverlay @@ -45,6 +46,7 @@ module DelegateToGeometry # engine and features. class Feature include DelegateToGeometry + include ConversionMethods # Create a feature wrapping the given geometry, with the given ID # and properties. @@ -123,6 +125,7 @@ def keys class FeatureCollection include Enumerable include CollectionMethods + include ConversionMethods # Create a new FeatureCollection with the given features, which must # be provided as an Enumerable. diff --git a/test/basic_test.rb b/test/basic_test.rb index c9ce1b8..da78bf6 100644 --- a/test/basic_test.rb +++ b/test/basic_test.rb @@ -283,4 +283,49 @@ def test_feature_cast point_feature = RGeo::GeoJSON::Feature.new(factory.point(1, 1)) assert poly.contains?(point_feature) end + + def test_feature_to_geojson + json = { + "type" => "Feature", + "geometry" => { + "type" => "Polygon", + "coordinates" => [[[10.0, 20.0], [12.0, 22.0], [-3.0, 24.0], [10.0, 20.0]]] + }, + "properties" => {} + } + feature = RGeo::GeoJSON.decode(json) + assert_equal( + json, + feature.as_geojson + ) + assert_equal( + '{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[10.0,20.0],[12.0,22.0],[-3.0,24.0],[10.0,20.0]]]},"properties":{}}', + feature.to_geojson + ) + end + + def test_feature_collection_to_geojson + json = { + "type" => "FeatureCollection", + "features" => [ + { + "type" => "Feature", + "geometry" => { + "type" => "Point", + "coordinates" => [10.0, 20.0], + }, + "properties" => {} + } + ] + } + feature_collection = RGeo::GeoJSON.decode(json) + assert_equal( + json, + feature_collection.as_geojson + ) + assert_equal( + '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[10.0,20.0]},"properties":{}}]}', + feature_collection.to_geojson + ) + end end diff --git a/test/common/conversion_test.rb b/test/common/conversion_test.rb new file mode 100644 index 0000000..928edac --- /dev/null +++ b/test/common/conversion_test.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module RGeo + module GeoJSON # :nodoc: + module Tests # :nodoc: + module Common # :nodoc: + module ConversionTests # :nodoc: + def test_geometry_to_geojson + pt = @factory.point(1, 2) + assert_equal( + { "type" => "Point", "coordinates" => [1.0, 2.0] }, + pt.as_geojson + ) + assert_equal( + '{"type":"Point","coordinates":[1.0,2.0]}', + pt.to_geojson + ) + end + + def test_parse_geojson + pt = @factory.point(1, 2) + assert_equal(pt, @factory.parse_geojson(pt.as_geojson)) + assert_equal(pt, @factory.parse_geojson(pt.to_geojson)) + end + end + end + end + end +end diff --git a/test/geos_capi/conversion_test.rb b/test/geos_capi/conversion_test.rb new file mode 100644 index 0000000..0ae076e --- /dev/null +++ b/test/geos_capi/conversion_test.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "minitest/autorun" +require_relative "../../lib/rgeo-geojson" +require_relative "../common/conversion_test" + +class GeosConversionTest < Minitest::Test # :nodoc: + include RGeo::GeoJSON::Tests::Common::ConversionTests + + def setup + @factory = RGeo::Geos.factory + end +end diff --git a/test/geos_ffi/conversion_test.rb b/test/geos_ffi/conversion_test.rb new file mode 100644 index 0000000..8e88a18 --- /dev/null +++ b/test/geos_ffi/conversion_test.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "minitest/autorun" +require_relative "../../lib/rgeo-geojson" +require_relative "../common/conversion_test" + +class GeosFFIConversionTest < Minitest::Test # :nodoc: + include RGeo::GeoJSON::Tests::Common::ConversionTests + + def setup + @factory = RGeo::Geos.factory(native_interface: :ffi) + end +end diff --git a/test/simple_cartesian/conversion_test.rb b/test/simple_cartesian/conversion_test.rb new file mode 100644 index 0000000..9c45e08 --- /dev/null +++ b/test/simple_cartesian/conversion_test.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "minitest/autorun" +require_relative "../../lib/rgeo-geojson" +require_relative "../common/conversion_test" + +class CartesianConversionTest < Minitest::Test # :nodoc: + include RGeo::GeoJSON::Tests::Common::ConversionTests + + def setup + @factory = RGeo::Cartesian.simple_factory + end +end diff --git a/test/simple_mercator/conversion_test.rb b/test/simple_mercator/conversion_test.rb new file mode 100644 index 0000000..1fd5592 --- /dev/null +++ b/test/simple_mercator/conversion_test.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "minitest/autorun" +require_relative "../../lib/rgeo-geojson" +require_relative "../common/conversion_test" + +class MercatorConversionTest < Minitest::Test # :nodoc: + include RGeo::GeoJSON::Tests::Common::ConversionTests + + def setup + @factory = RGeo::Geographic.simple_mercator_factory + end +end