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.
- checksums.yaml +4 -4
- data/charta.gemspec +21 -19
- data/lib/charta.rb +38 -89
- data/lib/charta/coordinates.rb +17 -17
- data/lib/charta/ewkt_serializer.rb +10 -1
- data/lib/charta/factory/ewkt_feature_builder.rb +17 -0
- data/lib/charta/factory/feature_factory_base.rb +15 -0
- data/lib/charta/factory/simple_feature_factory.rb +50 -0
- data/lib/charta/factory/simple_geometry_factory.rb +46 -0
- data/lib/charta/factory/srid_provider.rb +36 -0
- data/lib/charta/factory/transformers/ewkt_passthrough.rb +20 -0
- data/lib/charta/factory/transformers/ewkt_transformer.rb +20 -0
- data/lib/charta/factory/transformers/ewkt_transformer_chain.rb +42 -0
- data/lib/charta/factory/transformers/from_geo_json_transformer.rb +20 -0
- data/lib/charta/factory/transformers/from_gml_transformer.rb +20 -0
- data/lib/charta/factory/transformers/from_kml_transformer.rb +20 -0
- data/lib/charta/factory/transformers/from_wkb_transformer.rb +24 -0
- data/lib/charta/factory/transformers/transformation_error.rb +10 -0
- data/lib/charta/geo_json.rb +4 -1
- data/lib/charta/geometry.rb +45 -25
- data/lib/charta/geometry_collection.rb +6 -4
- data/lib/charta/gml.rb +18 -2
- data/lib/charta/gml_import.rb +24 -24
- data/lib/charta/kml.rb +21 -3
- data/lib/charta/multi_polygon.rb +1 -1
- data/lib/charta/point.rb +2 -1
- data/lib/charta/version.rb +1 -1
- data/lib/rgeo/svg.rb +44 -44
- metadata +84 -37
- data/.gitignore +0 -12
- data/.gitlab-ci.yml +0 -13
- data/.travis.yml +0 -7
- data/CODE_OF_CONDUCT.md +0 -74
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -21
- data/README.md +0 -44
- 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
|
data/lib/charta/geo_json.rb
CHANGED
@@ -48,7 +48,10 @@ module Charta
|
|
48
48
|
Coordinates.flatten hash
|
49
49
|
end
|
50
50
|
|
51
|
-
%i[
|
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
|
data/lib/charta/geometry.rb
CHANGED
@@ -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
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
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
|
-
#
|
266
|
-
# serialization in time.
|
266
|
+
# Returns the underlaying object managed by Charta: the RGeo feature
|
267
267
|
def feature
|
268
|
-
|
269
|
-
if @ewkt
|
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
|
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
|
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
|
-
|
5
|
-
srid =
|
6
|
-
|
7
|
-
|
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
|
-
|
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 =
|
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
|