charta 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +5 -5
  2. data/charta.gemspec +21 -21
  3. data/lib/charta.rb +39 -87
  4. data/lib/charta/bounding_box.rb +4 -0
  5. data/lib/charta/coordinates.rb +65 -0
  6. data/lib/charta/ewkt_serializer.rb +101 -0
  7. data/lib/charta/factory/ewkt_feature_builder.rb +17 -0
  8. data/lib/charta/factory/feature_factory_base.rb +15 -0
  9. data/lib/charta/factory/simple_feature_factory.rb +50 -0
  10. data/lib/charta/factory/simple_geometry_factory.rb +46 -0
  11. data/lib/charta/factory/srid_provider.rb +36 -0
  12. data/lib/charta/factory/transformers/ewkt_passthrough.rb +20 -0
  13. data/lib/charta/factory/transformers/ewkt_transformer.rb +20 -0
  14. data/lib/charta/factory/transformers/ewkt_transformer_chain.rb +42 -0
  15. data/lib/charta/factory/transformers/from_geo_json_transformer.rb +20 -0
  16. data/lib/charta/factory/transformers/from_gml_transformer.rb +20 -0
  17. data/lib/charta/factory/transformers/from_kml_transformer.rb +20 -0
  18. data/lib/charta/factory/transformers/from_wkb_transformer.rb +24 -0
  19. data/lib/charta/factory/transformers/transformation_error.rb +10 -0
  20. data/lib/charta/geo_json.rb +12 -124
  21. data/lib/charta/geometry.rb +111 -68
  22. data/lib/charta/geometry_collection.rb +6 -4
  23. data/lib/charta/gml.rb +18 -2
  24. data/lib/charta/gml_import.rb +24 -24
  25. data/lib/charta/kml.rb +21 -3
  26. data/lib/charta/multi_polygon.rb +1 -1
  27. data/lib/charta/point.rb +6 -0
  28. data/lib/charta/polygon.rb +5 -0
  29. data/lib/charta/version.rb +1 -1
  30. data/lib/rgeo/svg.rb +44 -44
  31. metadata +76 -55
  32. data/.gitignore +0 -11
  33. data/.travis.yml +0 -7
  34. data/CODE_OF_CONDUCT.md +0 -74
  35. data/Gemfile +0 -4
  36. data/LICENSE.txt +0 -21
  37. data/README.md +0 -44
  38. data/Rakefile +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 94a236f9258dcbbbaa9a215a5af25e1ddeb904bb
4
- data.tar.gz: 8251595d5fc8c7a70881108b37b43507b694f034
2
+ SHA256:
3
+ metadata.gz: a9634870840f818207264c46799dc53cf3e37bf3f3d119e85dc470405ebcb8d1
4
+ data.tar.gz: 6453c04a73201f197c869b37d18a6ff0c262c8545ced384a13ecee48568279e1
5
5
  SHA512:
6
- metadata.gz: 2440f9a26b7cd4d3d14c5f8b3840f3be1ba51ac7e11269c4ef2d5e615956d87f652b210b70861d803f11a1748533e67166c33732c946eac46f31e57470bf1496
7
- data.tar.gz: a426cc2d10067261ad61666d63373cb7e28be1ac6c5b4887926b44d1ae7efe8e3d089e4092d19e791d6b5dc9df9ee6bbd96faa687568ae8253c3e367b7bfc6a2
6
+ metadata.gz: 658472714de5dbe3a618839ddf36858884b6d28ab520bab2c5140514d0a287931b52aa1278110dc3979a3ed40b2bc0c7cb7e11a9450bff9767ae32180ebe24f8
7
+ data.tar.gz: 675ea3982c1d1bb3465f68e272fb995dde17cc61f5af81837346a7431fc79d1fc82dc0661eb2528aab2903808d6e0941d20923fa62142db25eafa95eb32a00f6
data/charta.gemspec CHANGED
@@ -1,30 +1,30 @@
1
- lib = File.expand_path('../lib', __FILE__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'charta/version'
1
+ require_relative 'lib/charta/version'
4
2
 
5
3
  Gem::Specification.new do |spec|
6
- spec.name = 'charta'
7
- spec.version = Charta::VERSION
8
- spec.authors = ['Brice TEXIER']
9
- spec.email = ['brice@ekylibre.com']
4
+ spec.name = 'charta'
5
+ spec.version = Charta::VERSION
6
+ spec.authors = ['Ekylibre developers']
7
+ spec.email = ['dev@ekylibre.com']
10
8
 
11
- spec.summary = 'Simple tool over geos and co'
12
- spec.homepage = 'https://github.com/ekylibre/charta'
13
- spec.license = 'MIT'
9
+ spec.summary = 'Simple tool over geos and co'
10
+ spec.required_ruby_version = '>= 2.6.0'
11
+ spec.homepage = 'https://gitlab.com/ekylibre'
12
+ spec.license = 'AGPL-3.0-only'
13
+
14
+ spec.files = Dir.glob(%w[lib/**/*.rb *.gemspec])
14
15
 
15
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
- f.match(%r{^(test|spec|features)/})
17
- end
18
16
  spec.require_paths = ['lib']
19
17
 
20
- spec.add_dependency 'nokogiri', '>= 1.7.0'
21
- spec.add_dependency 'rgeo', '~> 1.0.0'
18
+ spec.add_dependency 'activesupport', '~> 5.0'
22
19
  spec.add_dependency 'json', '>= 1.8.0'
23
- spec.add_dependency 'rgeo-geojson', '~> 1.0.0'
24
- spec.add_dependency 'rgeo-proj4', '~> 1.0.0'
25
- spec.add_dependency 'activesupport', '>= 5.1'
26
- spec.add_development_dependency 'bundler', '~> 1.14'
27
- spec.add_development_dependency 'rake', '~> 10.0'
20
+ spec.add_dependency 'nokogiri', '>= 1.7.0'
21
+ spec.add_dependency 'rgeo', '~> 2.0'
22
+ spec.add_dependency 'rgeo-geojson', '~> 2.0'
23
+ spec.add_dependency 'rgeo-proj4', '~> 2.0'
24
+ spec.add_dependency 'zeitwerk', '~> 2.4.0'
25
+
26
+ spec.add_development_dependency 'bundler', '~> 2.0'
28
27
  spec.add_development_dependency 'minitest', '~> 5.0'
29
- spec.add_development_dependency 'byebug'
28
+ spec.add_development_dependency 'rake', '~> 12.0'
29
+ spec.add_development_dependency 'rubocop', '1.3.1'
30
30
  end
data/lib/charta.rb CHANGED
@@ -1,17 +1,18 @@
1
1
  # Gathers geomatic calculations
2
2
  # Completes RGeo
3
+ require 'bigdecimal'
4
+ require 'bigdecimal/util'
3
5
  require 'rgeo'
4
6
  require 'rgeo/proj4'
5
- require 'charta/geometry'
6
- require 'charta/geometry_collection'
7
- require 'charta/point'
8
- require 'charta/line_string'
9
- require 'charta/polygon'
10
- require 'charta/multi_polygon'
11
- require 'charta/bounding_box'
12
- require 'charta/geo_json'
13
- require 'charta/gml'
14
- require 'charta/kml'
7
+ require 'zeitwerk'
8
+
9
+ loader = Zeitwerk::Loader.for_gem
10
+ loader.inflector.inflect(
11
+ 'geo_json' => 'GeoJSON',
12
+ 'gml' => 'GML',
13
+ 'kml' => 'KML'
14
+ )
15
+ loader.setup
15
16
 
16
17
  unless RGeo::CoordSys::Proj4.supported?
17
18
  puts "Proj4 is not supported. Some actions won't work"
@@ -26,75 +27,27 @@ module Charta
26
27
  }.freeze
27
28
 
28
29
  class << self
30
+ def default_feature_factory=(factory)
31
+ @default_feature_factory = factory
32
+ @geometry_factory = nil
33
+ end
34
+
35
+ # @deprecated This is deprecated and will be removed in 0.4
36
+ def default_feature_factory
37
+ @default_feature_factory || (self.default_feature_factory = Factory::SimpleFeatureFactory.build)
38
+ end
39
+
40
+ def geometry_factory
41
+ @geometry_factory ||= Factory::SimpleGeometryFactory.new(feature_factory: default_feature_factory)
42
+ end
43
+
44
+ # @deprecated This is deprecated and will be removed in 0.4
29
45
  def new_feature(coordinates, srs = nil, format = nil, _flatten_collection = true, _options = {})
30
- geom_ewkt = nil
31
- if coordinates.is_a?(RGeo::Feature::Instance)
32
- return Geometry.feature(coordinates)
33
- elsif coordinates.is_a?(::Charta::Geometry)
34
- return coordinates
35
- elsif coordinates.to_s =~ /\A[[:space:]]*\z/
36
- geom_ewkt = empty_geometry(srs).to_ewkt
37
- elsif coordinates.is_a?(Hash) || (coordinates.is_a?(String) && ::Charta::GeoJSON.valid?(coordinates)) # GeoJSON
38
- srid = srs ? find_srid(srs) : :WGS84
39
- geom_ewkt = ::Charta::GeoJSON.new(coordinates, srid).to_ewkt
40
- elsif coordinates.is_a?(String)
41
- geom_ewkt = if coordinates =~ /\A[A-F0-9]+\z/ # WKB
42
- if srs && srid = find_srid(srs)
43
- generate_ewkt RGeo::Geos.factory(srid: srid).parse_wkb(coordinates)
44
- else
45
- generate_ewkt Geometry.factory.parse_wkb(coordinates)
46
- end
47
- elsif format == 'gml' && ::Charta::GML.valid?(coordinates)
48
- # required format 'cause kml geometries return empty instead of failing
49
- ::Charta::GML.new(coordinates, srid).to_ewkt
50
- elsif format == 'kml' && ::Charta::KML.valid?(coordinates)
51
- ::Charta::KML.new(coordinates).to_ewkt
52
- elsif coordinates =~ /^SRID\=\d+\;/i
53
- if feature = Geometry.feature(coordinates)
54
- generate_ewkt feature
55
- else
56
- Charta::GeometryCollection.empty.feature
57
- end
58
- else # WKT expected
59
- if srs && srid = find_srid(srs)
60
- begin
61
- f = RGeo::Geos.factory(srid: srid).parse_wkt(coordinates)
62
- rescue RGeo::Error::ParseError => e
63
- raise "Invalid EWKT (#{e.message}): #{coordinates}"
64
- end
65
- generate_ewkt f
66
- else
67
- generate_ewkt Geometry.feature(coordinates)
68
- end
69
- end
70
- else # Default for RGeo
71
- geom_ewkt = generate_ewkt coordinates
72
- end
73
- if geom_ewkt.to_s =~ /\A[[:space:]]*\z/
74
- raise ArgumentError, "Invalid data: coordinates=#{coordinates.inspect}, srid=#{srid.inspect}"
75
- end
76
- Geometry.feature(geom_ewkt)
46
+ default_feature_factory.new_feature(coordinates, srs: srs, format: format)
77
47
  end
78
48
 
79
49
  def new_geometry(coordinates, srs = nil, format = nil, _flatten_collection = true, _options = {})
80
- return coordinates if coordinates.is_a?(::Charta::Geometry)
81
- feature = Charta.new_feature(coordinates, srs, format, _flatten_collection, _options)
82
- type = feature.geometry_type
83
- geom = case type
84
- when RGeo::Feature::Point then
85
- Point.new(feature)
86
- when RGeo::Feature::LineString then
87
- LineString.new(feature)
88
- when RGeo::Feature::Polygon then
89
- Polygon.new(feature)
90
- when RGeo::Feature::MultiPolygon then
91
- MultiPolygon.new(feature)
92
- when RGeo::Feature::GeometryCollection then
93
- GeometryCollection.new(feature)
94
- else
95
- Geometry.new(feature)
96
- end
97
- geom
50
+ geometry_factory.new_geometry(coordinates, srs: srs, format: format)
98
51
  end
99
52
 
100
53
  def new_point(lat, lon, srid = 4326)
@@ -106,12 +59,18 @@ module Charta
106
59
  options[:srid] ||= new_geometry(points.first).srid if points.any?
107
60
  options[:srid] ||= 4326
108
61
 
109
- ewkt = "SRID=#{options[:srid]};LINESTRING(" + points.map { |wkt| p = new_geometry(wkt); "#{p.x} #{p.y}" }.join(', ') + ')'
62
+ points_coordinates = points.map do |wkt|
63
+ p = new_geometry(wkt)
64
+
65
+ "#{p.x} #{p.y}"
66
+ end
67
+
68
+ ewkt = "SRID=#{options[:srid]};LINESTRING(#{points_coordinates.join(', ')})"
110
69
  new_geometry(ewkt)
111
70
  end
112
71
 
113
72
  def empty_geometry(srid = :WGS84)
114
- GeometryCollection.empty(srid)
73
+ geometry_factory.empty_geometry(srid)
115
74
  end
116
75
 
117
76
  def generate_ewkt(feature)
@@ -138,23 +97,16 @@ module Charta
138
97
  end
139
98
 
140
99
  # Check and returns the SRID matching with srname or SRID.
100
+ # @deprecated
141
101
  def find_srid(srname_or_srid)
142
- if srname_or_srid.to_s =~ /\Aurn:ogc:def:crs:.*\z/
143
- x = srname_or_srid.split(':').last.upcase.to_sym
144
- SRS[x] || x
145
- elsif srname_or_srid.to_s =~ /\AEPSG::?(\d{4,5})\z/
146
- srname_or_srid.split(':').last
147
- elsif srname_or_srid.to_s =~ /\A\d+\z/
148
- srname_or_srid.to_i
149
- else
150
- SRS[srname_or_srid] || srname_or_srid
151
- end
102
+ Factory::SridProvider.build.find(srname_or_srid)
152
103
  end
153
104
 
154
105
  def from(format, data)
155
106
  unless respond_to?("from_#{format}")
156
107
  raise "Unknown format: #{format.inspect}"
157
108
  end
109
+
158
110
  send("from_#{format}", data)
159
111
  end
160
112
 
@@ -24,5 +24,9 @@ module Charta
24
24
  def to_a
25
25
  [[@y_min, @x_min], [@y_max, @x_max]]
26
26
  end
27
+
28
+ def to_bbox_string
29
+ "#{@x_min}, #{@y_min}, #{x_max}, #{y_max}"
30
+ end
27
31
  end
28
32
  end
@@ -0,0 +1,65 @@
1
+ module Charta
2
+ module Coordinates
3
+ class << self
4
+
5
+ # Force coordinates to 2D
6
+ def flatten(hash)
7
+ map_coordinates(hash) { |position| position[0..1] }
8
+ end
9
+
10
+ def map_coordinates(hash, &block)
11
+ case hash['type']
12
+ when 'FeatureCollection'
13
+ map_feature_collection_coordinates hash, &block
14
+ when 'Feature'
15
+ map_feature_coordinates hash, &block
16
+ else
17
+ map_geometry_coordinates hash, &block
18
+ end
19
+ end
20
+
21
+ def normalize_4326_geometry(json)
22
+ map_coordinates json do |(x, y)|
23
+ [((x + 180.to_d) % 360.to_d) - 180.to_d, ((y + 90.to_d) % 180.to_d) - 90.to_d]
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def map_feature_collection_coordinates(hash, &block)
30
+ hash.merge 'features' => hash['features'].map { |feature| map_feature_coordinates feature, &block }
31
+ end
32
+
33
+ def map_feature_coordinates(hash, &block)
34
+ hash.merge 'geometry' => map_geometry_coordinates(hash['geometry'], &block)
35
+ end
36
+
37
+ def map_geometry_coordinates(hash, &block)
38
+ if hash['type'] == 'GeometryCollection'
39
+ map_geometry_collection_coordinates hash, &block
40
+ else
41
+ coordinates = hash['coordinates']
42
+ mapped =
43
+ case hash['type']
44
+ when 'Point'
45
+ block.call coordinates
46
+ when 'MultiPoint', 'LineString'
47
+ coordinates.map(&block)
48
+ when 'MultiLineString', 'Polygon'
49
+ coordinates.map { |line| line.map(&block) }
50
+ when 'MultiPolygon'
51
+ coordinates.map { |poly| poly.map { |line| line.map(&block) } }
52
+ else
53
+ raise StandardError.new("Cannot handle: #{hash['type'].inspect}. In #{hash.inspect}")
54
+ end
55
+
56
+ hash.merge 'coordinates' => mapped
57
+ end
58
+ end
59
+
60
+ def map_geometry_collection_coordinates(hash, &block)
61
+ hash.merge 'geometries' => hash['geometries'].map { |geometry| map_geometry_coordinates(geometry, &block) }
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,101 @@
1
+ module Charta
2
+ module EwktSerializer
3
+
4
+ class << self
5
+ def object_to_ewkt(hash)
6
+ type = hash[:type] || hash['type']
7
+ send("#{type.gsub(/(.)([A-Z])/, '\1_\2').downcase}_to_ewkt", hash)
8
+ end
9
+
10
+ private
11
+
12
+ def feature_collection_to_ewkt(hash)
13
+ return 'GEOMETRYCOLLECTION EMPTY' if hash['features'].nil?
14
+
15
+ 'GEOMETRYCOLLECTION(' + hash['features'].collect do |feature|
16
+ object_to_ewkt(feature)
17
+ end.join(', ') + ')'
18
+ end
19
+
20
+ def geometry_collection_to_ewkt(hash)
21
+ return 'GEOMETRYCOLLECTION EMPTY' if hash['geometries'].nil?
22
+
23
+ 'GEOMETRYCOLLECTION(' + hash['geometries'].collect do |feature|
24
+ object_to_ewkt(feature)
25
+ end.join(', ') + ')'
26
+ end
27
+
28
+ def feature_to_ewkt(hash)
29
+ object_to_ewkt(hash['geometry'])
30
+ end
31
+
32
+ def point_to_ewkt(hash)
33
+ return 'POINT EMPTY' if hash['coordinates'].nil?
34
+
35
+ 'POINT(' + hash['coordinates'].join(' ') + ')'
36
+ end
37
+
38
+ def line_string_to_ewkt(hash)
39
+ return 'LINESTRING EMPTY' if hash['coordinates'].nil?
40
+
41
+ 'LINESTRING(' + hash['coordinates'].collect do |point|
42
+ point.join(' ')
43
+ end.join(', ') + ')'
44
+ end
45
+
46
+ def polygon_to_ewkt(hash)
47
+ return 'POLYGON EMPTY' if hash['coordinates'].nil?
48
+
49
+ 'POLYGON(' + hash['coordinates'].collect do |hole|
50
+ '(' + hole.collect do |point|
51
+ point.join(' ')
52
+ end.join(', ') + ')'
53
+ end.join(', ') + ')'
54
+ end
55
+
56
+ def multi_point_to_ewkt(hash)
57
+ return 'MULTIPOINT EMPTY' if hash['coordinates'].nil?
58
+
59
+ 'MULTIPOINT(' + hash['coordinates'].collect do |point|
60
+ '(' + point.join(' ') + ')'
61
+ end.join(', ') + ')'
62
+ end
63
+
64
+ def multi_line_string_to_ewkt(hash)
65
+ return 'MULTILINESTRING EMPTY' if hash['coordinates'].nil?
66
+
67
+ 'MULTILINESTRING(' + hash['coordinates'].collect do |line|
68
+ '(' + line.collect do |point|
69
+ point.join(' ')
70
+ end.join(', ') + ')'
71
+ end.join(', ') + ')'
72
+ end
73
+
74
+ def multipolygon_to_ewkt(hash)
75
+ return 'MULTIPOLYGON EMPTY' if hash['coordinates'].nil?
76
+
77
+ 'MULTIPOLYGON(' + hash['coordinates'].collect do |polygon|
78
+ '(' + polygon.collect do |hole|
79
+ '(' + hole.collect do |point|
80
+ point.join(' ')
81
+ end.join(', ') + ')'
82
+ end.join(', ') + ')'
83
+ end.join(', ') + ')'
84
+ end
85
+
86
+ # for PostGIS ST_ASGeoJSON compatibility
87
+ def multi_polygon_to_ewkt(hash)
88
+ return 'MULTIPOLYGON EMPTY' if hash['coordinates'].nil?
89
+
90
+ 'MULTIPOLYGON(' + hash['coordinates'].collect do |polygon|
91
+ '(' + polygon.collect do |hole|
92
+ '(' + hole.collect do |point|
93
+ point.join(' ')
94
+ end.join(', ') + ')'
95
+ end.join(', ') + ')'
96
+ end.join(', ') + ')'
97
+ end
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Charta
4
+ module Factory
5
+ class EwktFeatureBuilder
6
+ # @param [String] ewkt EWKT representation of a feature
7
+ # @return [RGeo::Feature::Instance]
8
+ def from_ewkt(ewkt)
9
+ if ewkt.to_s =~ /\A[[:space:]]*\z/
10
+ raise ArgumentError.new("Invalid data: #{ewkt.inspect}")
11
+ end
12
+
13
+ Geometry.feature(ewkt)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Charta
4
+ module Factory
5
+ class FeatureFactoryBase
6
+ def new_feature(coordinates, srs: nil, format: nil)
7
+ raise StandardError.new('Not implemented')
8
+ end
9
+
10
+ def empty_feature(srs = :WGS84)
11
+ new_feature('GEOMETRYCOLLECTION EMPTY', srs: srs)
12
+ end
13
+ end
14
+ end
15
+ end