charta 0.1.18 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|