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
@@ -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