charta 0.2.0 → 0.3.1

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 (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
@@ -6,8 +6,6 @@ require 'active_support/core_ext/module/delegation'
6
6
  module Charta
7
7
  # Represents a Geometry with SRID
8
8
  class Geometry
9
- delegate_missing_to :to_rgeo
10
-
11
9
  def initialize(feature, properties = {})
12
10
  self.feature = feature
13
11
  @properties = properties
@@ -36,16 +34,12 @@ module Charta
36
34
  feature.srid.to_i
37
35
  end
38
36
 
39
- # Returns the underlaying object managed by Charta: the RGeo feature
40
- def to_rgeo
41
- feature
42
- end
43
-
44
37
  # Returns the Well-Known Text (WKT) representation of the geometry/geography
45
38
  # without SRID metadata
46
39
  def to_text
47
40
  feature.as_text.match(/\ASRID=.*;(.*)/)[1]
48
41
  end
42
+
49
43
  alias as_text to_text
50
44
  alias to_wkt to_text
51
45
 
@@ -53,6 +47,7 @@ module Charta
53
47
  def to_ewkt
54
48
  Charta.generate_ewkt(feature).to_s
55
49
  end
50
+
56
51
  alias to_s to_ewkt
57
52
 
58
53
  def ewkt
@@ -65,6 +60,7 @@ module Charta
65
60
  generator = RGeo::WKRep::WKBGenerator.new(tag_format: :ewkbt, emit_ewkbt_srid: true)
66
61
  generator.generate(feature)
67
62
  end
63
+
68
64
  alias to_ewkb to_binary
69
65
 
70
66
  # Pas bien compris le fonctionnement
@@ -88,6 +84,7 @@ module Charta
88
84
  def to_geojson
89
85
  to_json_object.to_json
90
86
  end
87
+
91
88
  alias to_json to_geojson
92
89
 
93
90
  # Returns object in JSON (Hash)
@@ -100,6 +97,7 @@ module Charta
100
97
  other_geometry = Charta.new_geometry(other).transform(srid)
101
98
  return true if empty? && other_geometry.empty?
102
99
  return inspect == other_geometry.inspect if collection? && other_geometry.collection?
100
+
103
101
  feature.equals?(other_geometry.feature)
104
102
  end
105
103
 
@@ -108,17 +106,30 @@ module Charta
108
106
  other_geometry = Charta.new_geometry(other).transform(srid)
109
107
  return true if empty? && other_geometry.empty?
110
108
  return inspect == other_geometry.inspect if collection? && other_geometry.collection?
109
+
111
110
  !feature.equals?(other_geometry.feature)
112
111
  end
113
112
 
114
113
  # Returns true if Geometry is a Surface
115
114
  def surface?
116
- [RGeo::Feature::Polygon, RGeo::Feature::MultiPolygon].include? feature.geometry_type
115
+ if collection?
116
+ feature.any? { |geometry| Charta.new_geometry(geometry).surface? }
117
+ else
118
+ [RGeo::Feature::Polygon, RGeo::Feature::MultiPolygon].include? feature.geometry_type
119
+ end
117
120
  end
118
121
 
119
122
  # Returns area in unit corresponding to the SRS
120
123
  def area
121
- surface? ? feature.area : 0
124
+ if surface?
125
+ if collection?
126
+ feature.sum { |geometry| Charta.new_geometry(geometry).area }
127
+ else
128
+ feature.area
129
+ end
130
+ else
131
+ 0
132
+ end
122
133
  end
123
134
 
124
135
  # Returns true if this Geometry is an empty geometrycollection, polygon,
@@ -126,12 +137,14 @@ module Charta
126
137
  def empty?
127
138
  feature.is_empty?
128
139
  end
140
+
129
141
  alias blank? empty?
130
142
 
131
143
  # Computes the geometric center of a geometry, or equivalently, the center
132
144
  # of mass of the geometry as a POINT.
133
145
  def centroid
134
146
  return nil unless surface? && !feature.is_empty?
147
+
135
148
  point = feature.centroid
136
149
  [point.y, point.x]
137
150
  end
@@ -139,16 +152,23 @@ module Charta
139
152
  # Returns a POINT guaranteed to lie on the surface.
140
153
  def point_on_surface
141
154
  return nil unless surface?
155
+
142
156
  point = feature.point_on_surface
143
157
  [point.y, point.x]
144
158
  end
145
159
 
146
160
  def convert_to(new_type)
147
161
  case new_type
148
- when type then self
149
- when :multi_point then flatten_multi(:point)
150
- when :multi_line_string then flatten_multi(:line_string)
151
- when :multi_polygon then flatten_multi(:polygon)
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
152
172
  end
153
173
  end
154
174
 
@@ -176,10 +196,12 @@ module Charta
176
196
  def transform(new_srid)
177
197
  return self if new_srid == srid
178
198
  raise 'Proj is not supported. Cannot tranform' unless RGeo::CoordSys::Proj4.supported?
199
+
179
200
  new_srid = Charta::SRS[new_srid] || new_srid
180
201
  database = self.class.srs_database
181
202
  new_proj_entry = database.get(new_srid)
182
203
  raise "Cannot find proj for SRID: #{new_srid}" if new_proj_entry.nil?
204
+
183
205
  new_feature = RGeo::CoordSys::Proj4.transform(
184
206
  database.get(srid).proj4,
185
207
  feature,
@@ -199,6 +221,7 @@ module Charta
199
221
  other_geometry = Charta.new_geometry(other).transform(srid)
200
222
  feature.union(other_geometry.feature)
201
223
  end
224
+
202
225
  alias + merge
203
226
 
204
227
  def intersection(other)
@@ -206,10 +229,16 @@ module Charta
206
229
  feature.intersection(other_geometry.feature)
207
230
  end
208
231
 
232
+ def intersects?(other)
233
+ other_geometry = Charta.new_geometry(other).transform(srid)
234
+ feature.intersects?(other_geometry.feature)
235
+ end
236
+
209
237
  def difference(other)
210
238
  other_geometry = Charta.new_geometry(other).transform(srid)
211
239
  feature.difference(other_geometry.feature)
212
240
  end
241
+
213
242
  alias - difference
214
243
 
215
244
  def bounding_box
@@ -234,22 +263,24 @@ module Charta
234
263
  Charta.find_srid(name_or_srid)
235
264
  end
236
265
 
237
- # TODO: Manage YAML domain type to ensure maintainability of YAML
238
- # serialization in time.
266
+ # Returns the underlaying object managed by Charta: the RGeo feature
239
267
  def feature
240
268
  unless defined? @feature
241
269
  if defined? @ewkt
242
270
  @feature = ::Charta::Geometry.from_ewkt(@ewkt)
243
271
  @properties = @options.dup if @options
244
272
  else
245
- raise StandardError, 'Invalid geometry (no feature, no EWKT)'
273
+ raise StandardError.new('Invalid geometry (no feature, no EWKT)')
246
274
  end
247
275
  end
248
276
  @feature.dup
249
277
  end
250
278
 
279
+ alias to_rgeo feature
280
+
251
281
  def feature=(new_feature)
252
- 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
+
253
284
  @feature = new_feature
254
285
  end
255
286
 
@@ -257,6 +288,21 @@ module Charta
257
288
  { type: 'Feature', properties: properties, geometry: to_json_object }
258
289
  end
259
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
+
260
306
  class << self
261
307
  def srs_database
262
308
  @srs_database ||= RGeo::CoordSys::SRSDatabase::Proj4Data.new('epsg', authority: 'EPSG', cache: true)
@@ -264,11 +310,13 @@ module Charta
264
310
 
265
311
  def factory(srid = 4326)
266
312
  return projected_factory(srid) if srid.to_i == 4326
313
+
267
314
  geos_factory(srid)
268
315
  end
269
316
 
270
317
  def feature(ewkt_or_rgeo)
271
318
  return from_rgeo(ewkt_or_rgeo) if ewkt_or_rgeo.is_a? RGeo::Feature::Instance
319
+
272
320
  from_ewkt(ewkt_or_rgeo)
273
321
  end
274
322
 
@@ -279,7 +327,7 @@ module Charta
279
327
 
280
328
  def from_ewkt(ewkt)
281
329
  # Cleans empty geometries
282
- ewkt.gsub!(/(GEOMETRYCOLLECTION|GEOMETRY|((MULTI)?(POINT|LINESTRING|POLYGON)))\(\)/, '\1 EMPTY')
330
+ ewkt = ewkt.gsub(/(GEOMETRYCOLLECTION|GEOMETRY|((MULTI)?(POINT|LINESTRING|POLYGON)))\(\)/, '\1 EMPTY')
283
331
  srs = ewkt.split(/[\=\;]+/)[0..1]
284
332
  srid = nil
285
333
  srid = srs[1] if srs[0] =~ /srid/i
@@ -291,57 +339,52 @@ module Charta
291
339
 
292
340
  private
293
341
 
294
- def geos_factory(srid)
295
- RGeo::Geos.factory(
296
- srid: srid,
297
- wkt_generator: {
298
- type_format: :ewkt,
299
- emit_ewkt_srid: true,
300
- convert_case: :upper
301
- },
302
- wkt_parser: {
303
- support_ewkt: true
304
- },
305
- wkb_generator: {
306
- type_format: :ewkb,
307
- emit_ewkb_srid: true,
308
- hex_format: true
309
- },
310
- wkb_parser: {
311
- support_ewkb: true
312
- }
313
- )
314
- end
315
-
316
- def projected_factory(srid)
317
- proj4 = '+proj=cea +lon_0=0 +lat_ts=30 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs'
318
- RGeo::Geographic.projected_factory(
319
- srid: srid,
320
- wkt_generator: {
321
- type_format: :ewkt,
322
- emit_ewkt_srid: true,
323
- convert_case: :upper
324
- },
325
- wkt_parser: {
326
- support_ewkt: true
327
- },
328
- wkb_generator: {
329
- type_format: :ewkb,
330
- emit_ewkb_srid: true,
331
- hex_format: true
332
- },
333
- wkb_parser: {
334
- support_ewkb: true
335
- },
336
- projection_srid: 6933,
337
- projection_proj4: proj4
338
- )
339
- end
340
- end
342
+ def geos_factory(srid)
343
+ RGeo::Geos.factory(
344
+ srid: srid,
345
+ wkt_generator: {
346
+ type_format: :ewkt,
347
+ emit_ewkt_srid: true,
348
+ convert_case: :upper
349
+ },
350
+ wkt_parser: {
351
+ support_ewkt: true
352
+ },
353
+ wkb_generator: {
354
+ type_format: :ewkb,
355
+ emit_ewkb_srid: true,
356
+ hex_format: true
357
+ },
358
+ wkb_parser: {
359
+ support_ewkb: true
360
+ }
361
+ )
362
+ end
341
363
 
342
- def respond_to_missing?(name, include_private = false)
343
- return false if name == :init_with
344
- super
364
+ def projected_factory(srid)
365
+ proj4 = '+proj=cea +lon_0=0 +lat_ts=30 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs'
366
+ RGeo::Geographic.projected_factory(
367
+ srid: srid,
368
+ wkt_generator: {
369
+ type_format: :ewkt,
370
+ emit_ewkt_srid: true,
371
+ convert_case: :upper
372
+ },
373
+ wkt_parser: {
374
+ support_ewkt: true
375
+ },
376
+ wkb_generator: {
377
+ type_format: :ewkb,
378
+ emit_ewkb_srid: true,
379
+ hex_format: true
380
+ },
381
+ wkb_parser: {
382
+ support_ewkb: true
383
+ },
384
+ projection_srid: 6933,
385
+ projection_proj4: proj4
386
+ )
387
+ end
345
388
  end
346
389
  end
347
390
  end
@@ -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
@@ -63,30 +63,30 @@ class GmlImport
63
63
 
64
64
  private
65
65
 
66
- def featurize(node)
67
- if node.element? && node.xpath('.//gml:Polygon')
68
- geojson_feature = {}
69
-
70
- geometry = node.xpath('.//gml:Polygon')
71
- geometry.first['srsName'] = 'EPSG:2154'
72
-
73
- if ::Charta::GML.valid?(geometry)
74
-
75
- # properties
76
- id = (Time.zone.now.to_i.to_s + Time.zone.now.usec.to_s)
77
-
78
- geojson_feature = {
79
- type: 'Feature',
80
- properties: {
81
- internal_id: id
82
- }.reject { |_, v| v.nil? },
83
- geometry: ::Charta.new_geometry(geometry.to_xml, nil, 'gml').transform(:WGS84).to_geojson
84
- }.reject { |_, v| v.nil? }
85
-
86
- return geojson_feature
87
- else
88
- return false
66
+ def featurize(node)
67
+ if node.element? && node.xpath('.//gml:Polygon')
68
+ geojson_feature = {}
69
+
70
+ geometry = node.xpath('.//gml:Polygon')
71
+ geometry.first['srsName'] = 'EPSG:2154'
72
+
73
+ if ::Charta::GML.valid?(geometry)
74
+
75
+ # properties
76
+ id = (Time.zone.now.to_i.to_s + Time.zone.now.usec.to_s)
77
+
78
+ geojson_feature = {
79
+ type: 'Feature',
80
+ properties: {
81
+ internal_id: id
82
+ }.reject { |_, v| v.nil? },
83
+ geometry: ::Charta.new_geometry(geometry.to_xml, nil, 'gml').transform(:WGS84).to_geojson
84
+ }.reject { |_, v| v.nil? }
85
+
86
+ return geojson_feature
87
+ else
88
+ return false
89
+ end
89
90
  end
90
91
  end
91
- end
92
92
  end