charta 0.1.18 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/charta.gemspec +21 -19
  3. data/lib/charta.rb +38 -89
  4. data/lib/charta/coordinates.rb +17 -17
  5. data/lib/charta/ewkt_serializer.rb +10 -1
  6. data/lib/charta/factory/ewkt_feature_builder.rb +17 -0
  7. data/lib/charta/factory/feature_factory_base.rb +15 -0
  8. data/lib/charta/factory/simple_feature_factory.rb +50 -0
  9. data/lib/charta/factory/simple_geometry_factory.rb +46 -0
  10. data/lib/charta/factory/srid_provider.rb +36 -0
  11. data/lib/charta/factory/transformers/ewkt_passthrough.rb +20 -0
  12. data/lib/charta/factory/transformers/ewkt_transformer.rb +20 -0
  13. data/lib/charta/factory/transformers/ewkt_transformer_chain.rb +42 -0
  14. data/lib/charta/factory/transformers/from_geo_json_transformer.rb +20 -0
  15. data/lib/charta/factory/transformers/from_gml_transformer.rb +20 -0
  16. data/lib/charta/factory/transformers/from_kml_transformer.rb +20 -0
  17. data/lib/charta/factory/transformers/from_wkb_transformer.rb +24 -0
  18. data/lib/charta/factory/transformers/transformation_error.rb +10 -0
  19. data/lib/charta/geo_json.rb +4 -1
  20. data/lib/charta/geometry.rb +45 -25
  21. data/lib/charta/geometry_collection.rb +6 -4
  22. data/lib/charta/gml.rb +18 -2
  23. data/lib/charta/gml_import.rb +24 -24
  24. data/lib/charta/kml.rb +21 -3
  25. data/lib/charta/multi_polygon.rb +1 -1
  26. data/lib/charta/point.rb +2 -1
  27. data/lib/charta/version.rb +1 -1
  28. data/lib/rgeo/svg.rb +44 -44
  29. metadata +84 -37
  30. data/.gitignore +0 -12
  31. data/.gitlab-ci.yml +0 -13
  32. data/.travis.yml +0 -7
  33. data/CODE_OF_CONDUCT.md +0 -74
  34. data/Gemfile +0 -4
  35. data/LICENSE.txt +0 -21
  36. data/README.md +0 -44
  37. data/Rakefile +0 -10
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Charta
4
+ module Factory
5
+ class SridProvider
6
+ SRS = {
7
+ WGS84: 4326,
8
+ CRS84: 4326,
9
+ RGF93: 2143
10
+ }.freeze
11
+
12
+ class << self
13
+ def build
14
+ new(SRS)
15
+ end
16
+ end
17
+
18
+ def initialize(srs)
19
+ @srs = srs
20
+ end
21
+
22
+ def find(srname_or_srid)
23
+ if srname_or_srid.to_s =~ /\Aurn:ogc:def:crs:.*\z/
24
+ x = srname_or_srid.split(':').last.upcase.to_sym
25
+ @srs[x] || x
26
+ elsif srname_or_srid.to_s =~ /\AEPSG::?(\d{4,5})\z/
27
+ srname_or_srid.split(':').last
28
+ elsif srname_or_srid.to_s =~ /\A\d+\z/
29
+ srname_or_srid.to_i
30
+ else
31
+ @srs[srname_or_srid] || srname_or_srid
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Charta
4
+ module Factory
5
+ module Transformers
6
+ class EwktPassthrough < EwktTransformer
7
+ # @return [Boolean]
8
+ def handles?(value, format:)
9
+ value.is_a?(String) && format.nil?
10
+ end
11
+
12
+ # @param [String, Hash] value
13
+ # @return [String] ewkt representation of value
14
+ def transform(value, srid: nil, format: nil)
15
+ value
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Charta
4
+ module Factory
5
+ module Transformers
6
+ class EwktTransformer
7
+ # @return [Boolean]
8
+ def handles?(value, format:)
9
+ false
10
+ end
11
+
12
+ # @param [String, Hash] value
13
+ # @return [String] ewkt representation of value
14
+ def transform(value, srid: nil, format: nil)
15
+ raise StandardError.new('Not implemented')
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Charta
4
+ module Factory
5
+ module Transformers
6
+ class EwktTransformerChain < EwktTransformer
7
+ class << self
8
+ def build
9
+ new(
10
+ Transformers::FromGeoJsonTransformer.new,
11
+ Transformers::FromWkbTransformer.new,
12
+ Transformers::FromGmlTransformer.new,
13
+ Transformers::FromKmlTransformer.new,
14
+ Transformers::EwktPassthrough.new
15
+ )
16
+ end
17
+ end
18
+
19
+ # @return [Array<EwktTransformer>]
20
+ attr_reader :transformers
21
+
22
+ def initialize(*transformers)
23
+ @transformers = transformers
24
+ end
25
+
26
+ # @return [Boolean]
27
+ def handles?(value, format:)
28
+ transformers.any? { |t| t.handles?(value, format: format) }
29
+ end
30
+
31
+ # @param [String, Hash] value
32
+ # @return [String] ewkt representation of value
33
+ def transform(value, srid: nil, format: nil)
34
+ transformer = transformers.detect { |t| t.handles?(value, format: format) }
35
+ raise TransformationError.new('Not handled') if transformer.nil?
36
+
37
+ transformer.transform(value, srid: srid, format: format)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Charta
4
+ module Factory
5
+ module Transformers
6
+ class FromGeoJsonTransformer < EwktTransformer
7
+ # @return [Boolean]
8
+ def handles?(value, format:)
9
+ value.is_a?(Hash) || (value.is_a?(String) && Charta::GeoJSON.valid?(value)) # GeoJSON
10
+ end
11
+
12
+ # @param [String, Hash] value
13
+ # @return [String] ewkt representation of value
14
+ def transform(value, srid: nil, format: nil)
15
+ Charta::GeoJSON.new(value, srid).to_ewkt
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Charta
4
+ module Factory
5
+ module Transformers
6
+ class FromGmlTransformer < EwktTransformer
7
+ # @return [Boolean]
8
+ def handles?(value, format:)
9
+ value.is_a?(String) && format == 'gml' && Charta::GML.valid?(value)
10
+ end
11
+
12
+ # @param [String, Hash] value
13
+ # @return [String] ewkt representation of value
14
+ def transform(value, srid: nil, format: nil)
15
+ Charta::GML.new(value, srid).to_ewkt
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Charta
4
+ module Factory
5
+ module Transformers
6
+ class FromKmlTransformer < EwktTransformer
7
+ # @return [Boolean]
8
+ def handles?(value, format:)
9
+ value.is_a?(String) && format == 'kml' && Charta::KML.valid?(value)
10
+ end
11
+
12
+ # @param [String, Hash] value
13
+ # @return [String] ewkt representation of value
14
+ def transform(value, srid: nil, format: nil)
15
+ Charta::KML.new(value, srid).to_ewkt
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Charta
4
+ module Factory
5
+ module Transformers
6
+ class FromWkbTransformer < EwktTransformer
7
+ # @return [Boolean]
8
+ def handles?(value, format:)
9
+ value.is_a?(String) && !!(value =~ /\A[A-F0-9]+\z/)
10
+ end
11
+
12
+ # @param [String, Hash] value
13
+ # @return [String] ewkt representation of value
14
+ def transform(value, srid: nil, format: nil)
15
+ if srid.nil?
16
+ Geometry.factory.parse_wkb(value)
17
+ else
18
+ RGeo::Geos.factory(srid: srid).parse_wkb(value)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Charta
4
+ module Factory
5
+ module Transformers
6
+ class TransformationError < StandardError
7
+ end
8
+ end
9
+ end
10
+ end
@@ -48,7 +48,10 @@ module Charta
48
48
  Coordinates.flatten hash
49
49
  end
50
50
 
51
- %i[object_to_ewkt feature_collection_to_ewkt geometry_collection_to_ewkt feature_to_ewkt point_to_ewkt line_string_to_ewkt polygon_to_ewkt multi_point_to_ewkt multi_line_string_to_ewkt multipolygon_to_ewkt multi_polygon_to_ewkt].each do |m|
51
+ %i[
52
+ object_to_ewkt feature_collection_to_ewkt geometry_collection_to_ewkt feature_to_ewkt point_to_ewkt line_string_to_ewkt
53
+ polygon_to_ewkt multi_point_to_ewkt multi_line_string_to_ewkt multipolygon_to_ewkt multi_polygon_to_ewkt
54
+ ].each do |m|
52
55
  define_method m do |*args|
53
56
  EwktSerializer.send m, *args
54
57
  end
@@ -1,6 +1,7 @@
1
1
  require 'json'
2
2
  require 'rgeo/geo_json'
3
3
  require 'rgeo/svg' # integrated lib for now
4
+ require 'active_support/core_ext/module/delegation'
4
5
 
5
6
  module Charta
6
7
  # Represents a Geometry with SRID
@@ -33,11 +34,6 @@ module Charta
33
34
  feature.srid.to_i
34
35
  end
35
36
 
36
- # Returns the underlaying object managed by Charta: the RGeo feature
37
- def to_rgeo
38
- feature
39
- end
40
-
41
37
  # Returns the Well-Known Text (WKT) representation of the geometry/geography
42
38
  # without SRID metadata
43
39
  def to_text
@@ -101,6 +97,7 @@ module Charta
101
97
  other_geometry = Charta.new_geometry(other).transform(srid)
102
98
  return true if empty? && other_geometry.empty?
103
99
  return inspect == other_geometry.inspect if collection? && other_geometry.collection?
100
+
104
101
  feature.equals?(other_geometry.feature)
105
102
  end
106
103
 
@@ -109,6 +106,7 @@ module Charta
109
106
  other_geometry = Charta.new_geometry(other).transform(srid)
110
107
  return true if empty? && other_geometry.empty?
111
108
  return inspect == other_geometry.inspect if collection? && other_geometry.collection?
109
+
112
110
  !feature.equals?(other_geometry.feature)
113
111
  end
114
112
 
@@ -146,6 +144,7 @@ module Charta
146
144
  # of mass of the geometry as a POINT.
147
145
  def centroid
148
146
  return nil unless surface? && !feature.is_empty?
147
+
149
148
  point = feature.centroid
150
149
  [point.y, point.x]
151
150
  end
@@ -153,22 +152,23 @@ module Charta
153
152
  # Returns a POINT guaranteed to lie on the surface.
154
153
  def point_on_surface
155
154
  return nil unless surface?
155
+
156
156
  point = feature.point_on_surface
157
157
  [point.y, point.x]
158
158
  end
159
159
 
160
160
  def convert_to(new_type)
161
161
  case new_type
162
- when type then
163
- self
164
- when :multi_point then
165
- flatten_multi(:point)
166
- when :multi_line_string then
167
- flatten_multi(:line_string)
168
- when :multi_polygon then
169
- flatten_multi(:polygon)
170
- else
171
- self
162
+ when type
163
+ self
164
+ when :multi_point
165
+ flatten_multi(:point)
166
+ when :multi_line_string
167
+ flatten_multi(:line_string)
168
+ when :multi_polygon
169
+ flatten_multi(:polygon)
170
+ else
171
+ self
172
172
  end
173
173
  end
174
174
 
@@ -196,10 +196,12 @@ module Charta
196
196
  def transform(new_srid)
197
197
  return self if new_srid == srid
198
198
  raise 'Proj is not supported. Cannot tranform' unless RGeo::CoordSys::Proj4.supported?
199
+
199
200
  new_srid = Charta::SRS[new_srid] || new_srid
200
201
  database = self.class.srs_database
201
202
  new_proj_entry = database.get(new_srid)
202
203
  raise "Cannot find proj for SRID: #{new_srid}" if new_proj_entry.nil?
204
+
203
205
  new_feature = RGeo::CoordSys::Proj4.transform(
204
206
  database.get(srid).proj4,
205
207
  feature,
@@ -224,8 +226,7 @@ module Charta
224
226
 
225
227
  def intersection(other)
226
228
  other_geometry = Charta.new_geometry(other).transform(srid)
227
-
228
- Charta.new_geometry(feature.intersection(other_geometry.feature))
229
+ feature.intersection(other_geometry.feature)
229
230
  end
230
231
 
231
232
  def intersects?(other)
@@ -262,22 +263,24 @@ module Charta
262
263
  Charta.find_srid(name_or_srid)
263
264
  end
264
265
 
265
- # TODO: Manage YAML domain type to ensure maintainability of YAML
266
- # serialization in time.
266
+ # Returns the underlaying object managed by Charta: the RGeo feature
267
267
  def feature
268
- if @feature.nil?
269
- if @ewkt.nil?
270
- raise StandardError, 'Invalid geometry (no feature, no EWKT)'
271
- else
268
+ unless defined? @feature
269
+ if defined? @ewkt
272
270
  @feature = ::Charta::Geometry.from_ewkt(@ewkt)
273
271
  @properties = @options.dup if @options
272
+ else
273
+ raise StandardError.new('Invalid geometry (no feature, no EWKT)')
274
274
  end
275
275
  end
276
276
  @feature.dup
277
277
  end
278
278
 
279
+ alias to_rgeo feature
280
+
279
281
  def feature=(new_feature)
280
- raise ArgumentError, "Feature can't be nil" if new_feature.nil?
282
+ raise ArgumentError.new("Feature can't be nil") if new_feature.nil?
283
+
281
284
  @feature = new_feature
282
285
  end
283
286
 
@@ -285,6 +288,21 @@ module Charta
285
288
  { type: 'Feature', properties: properties, geometry: to_json_object }
286
289
  end
287
290
 
291
+ def method_missing(name, *args, &block)
292
+ target = to_rgeo
293
+ if target.respond_to? name
294
+ target.send name, *args
295
+ else
296
+ raise StandardError.new("Method #{name} does not exist for #{self.class.name}")
297
+ end
298
+ end
299
+
300
+ def respond_to_missing?(name, include_private = false)
301
+ return false if name == :init_with
302
+
303
+ super
304
+ end
305
+
288
306
  class << self
289
307
  def srs_database
290
308
  @srs_database ||= RGeo::CoordSys::SRSDatabase::Proj4Data.new('epsg', authority: 'EPSG', cache: true)
@@ -292,11 +310,13 @@ module Charta
292
310
 
293
311
  def factory(srid = 4326)
294
312
  return projected_factory(srid) if srid.to_i == 4326
313
+
295
314
  geos_factory(srid)
296
315
  end
297
316
 
298
317
  def feature(ewkt_or_rgeo)
299
318
  return from_rgeo(ewkt_or_rgeo) if ewkt_or_rgeo.is_a? RGeo::Feature::Instance
319
+
300
320
  from_ewkt(ewkt_or_rgeo)
301
321
  end
302
322
 
@@ -307,7 +327,7 @@ module Charta
307
327
 
308
328
  def from_ewkt(ewkt)
309
329
  # Cleans empty geometries
310
- ewkt.gsub!(/(GEOMETRYCOLLECTION|GEOMETRY|((MULTI)?(POINT|LINESTRING|POLYGON)))\(\)/, '\1 EMPTY')
330
+ ewkt = ewkt.gsub(/(GEOMETRYCOLLECTION|GEOMETRY|((MULTI)?(POINT|LINESTRING|POLYGON)))\(\)/, '\1 EMPTY')
311
331
  srs = ewkt.split(/[\=\;]+/)[0..1]
312
332
  srid = nil
313
333
  srid = srs[1] if srs[0] =~ /srid/i
@@ -1,10 +1,12 @@
1
1
  module Charta
2
2
  # Represent a Geometry with contains other geometries
3
3
  class GeometryCollection < Geometry
4
- def self.empty(srid = nil)
5
- srid = Charta.find_srid(srid.nil? ? :WGS84 : srid)
6
- feature = Charta.new_feature('GEOMETRYCOLLECTION EMPTY', srid)
7
- new(feature)
4
+ class << self
5
+ def empty(srid = nil)
6
+ srid = Charta.find_srid(srid.nil? ? :WGS84 : srid)
7
+ feature = Charta.new_feature('GEOMETRYCOLLECTION EMPTY', srid)
8
+ new(feature)
9
+ end
8
10
  end
9
11
 
10
12
  def to_json_feature_collection(collection_properties = [])
data/lib/charta/gml.rb CHANGED
@@ -83,6 +83,7 @@ module Charta
83
83
  'GEOMETRYCOLLECTION(' + gml.css("#{GML_PREFIX}|featureMember").collect do |feature|
84
84
  TAGS.collect do |tag|
85
85
  next if feature.css("#{GML_PREFIX}|#{tag}").empty?
86
+
86
87
  feature.css("#{GML_PREFIX}|#{tag}").collect do |fragment|
87
88
  object_to_ewkt(fragment, srid)
88
89
  end.compact.join(', ')
@@ -90,6 +91,7 @@ module Charta
90
91
  end.compact.join(', ') + ')'
91
92
  end
92
93
  end
94
+
93
95
  alias geometry_collection_to_ewkt document_to_ewkt
94
96
 
95
97
  def transform(data, from_srid, to_srid)
@@ -101,8 +103,9 @@ module Charta
101
103
 
102
104
  wkt = 'POLYGON(' + %w[outerBoundaryIs innerBoundaryIs].collect do |boundary|
103
105
  next if gml.css("#{GML_PREFIX}|#{boundary}").empty?
106
+
104
107
  gml.css("#{GML_PREFIX}|#{boundary}").collect do |hole|
105
- '(' + hole.css("#{GML_PREFIX}|coordinates").collect { |coords| coords.content.split(/\r\n|\n| /) }.flatten.reject(&:empty?).collect { |c| c.split ',' }.collect { |dimension| %(#{dimension.first} #{dimension[1]}) }.join(', ') + ')'
108
+ "(#{transform_coordinates(hole)})"
106
109
  end.join(', ')
107
110
  end.compact.join(', ') + ')'
108
111
 
@@ -115,6 +118,7 @@ module Charta
115
118
 
116
119
  def point_to_ewkt(gml, srid)
117
120
  return 'POINT EMPTY' if gml.css("#{GML_PREFIX}|coordinates").nil?
121
+
118
122
  wkt = 'POINT(' + gml.css("#{GML_PREFIX}|coordinates").collect { |coords| coords.content.split ',' }.flatten.join(' ') + ')'
119
123
 
120
124
  unless gml['srsName'].nil? || Charta.find_srid(gml['srsName']).to_s == srid.to_s
@@ -127,7 +131,7 @@ module Charta
127
131
  def line_string_to_ewkt(gml, srid)
128
132
  return 'LINESTRING EMPTY' if gml.css("#{GML_PREFIX}|coordinates").nil?
129
133
 
130
- wkt = 'LINESTRING(' + gml.css("#{GML_PREFIX}|coordinates").collect { |coords| coords.content.split(/\r\n|\n| /) }.flatten.reject(&:empty?).collect { |c| c.split ',' }.collect { |dimension| %(#{dimension.first} #{dimension[1]}) }.join(', ') + ')'
134
+ wkt = "LINESTRING(#{transform_coordinates(gml)})"
131
135
 
132
136
  unless gml['srsName'].nil? || Charta.find_srid(gml['srsName']).to_s == srid.to_s
133
137
  wkt = transform(wkt, Charta.find_srid(gml['srsName']), srid)
@@ -135,6 +139,18 @@ module Charta
135
139
 
136
140
  wkt
137
141
  end
142
+
143
+ private
144
+
145
+ def transform_coordinates(coordinates)
146
+ coordinates.css("#{GML_PREFIX}|coordinates")
147
+ .collect { |coords| coords.content.split(/\r\n|\n| /) }
148
+ .flatten
149
+ .reject(&:empty?)
150
+ .collect { |c| c.split ',' }
151
+ .collect { |dimension| %(#{dimension.first} #{dimension[1]}) }
152
+ .join(', ')
153
+ end
138
154
  end
139
155
  end
140
156
  end