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.
- checksums.yaml +5 -5
- data/charta.gemspec +21 -21
- data/lib/charta.rb +39 -87
- data/lib/charta/bounding_box.rb +4 -0
- data/lib/charta/coordinates.rb +65 -0
- data/lib/charta/ewkt_serializer.rb +101 -0
- 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 +12 -124
- data/lib/charta/geometry.rb +111 -68
- 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 +6 -0
- data/lib/charta/polygon.rb +5 -0
- data/lib/charta/version.rb +1 -1
- data/lib/rgeo/svg.rb +44 -44
- metadata +76 -55
- data/.gitignore +0 -11
- 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,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Charta
|
4
|
+
module Factory
|
5
|
+
class SimpleFeatureFactory < FeatureFactoryBase
|
6
|
+
class << self
|
7
|
+
def build
|
8
|
+
new(
|
9
|
+
ewkt_builder: EwktFeatureBuilder.new,
|
10
|
+
srid_provider: SridProvider.build,
|
11
|
+
transformer: Transformers::EwktTransformerChain.build
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [EwktFeatureBuilder]
|
17
|
+
attr_reader :ewkt_builder
|
18
|
+
# @return [SridProvider]
|
19
|
+
attr_reader :srid_provider
|
20
|
+
# @return [Transformers::EwktTransformer]
|
21
|
+
attr_reader :transformer
|
22
|
+
|
23
|
+
def initialize(ewkt_builder:, srid_provider:, transformer:)
|
24
|
+
@ewkt_builder = ewkt_builder
|
25
|
+
@srid_provider = srid_provider
|
26
|
+
@transformer = transformer
|
27
|
+
end
|
28
|
+
|
29
|
+
def new_feature(coordinates, srs: nil, format: nil)
|
30
|
+
if coordinates.is_a?(Charta::Geometry)
|
31
|
+
coordinates
|
32
|
+
elsif coordinates.is_a?(RGeo::Feature::Instance)
|
33
|
+
new_feature(Charta.generate_ewkt(coordinates))
|
34
|
+
elsif coordinates.to_s =~ /\A[[:space:]]*\z/
|
35
|
+
empty_feature(srs)
|
36
|
+
else
|
37
|
+
convert_feature(coordinates, srs: srs, format: format)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def convert_feature(coordinates, srs: nil, format: nil)
|
44
|
+
srid = srs.nil? ? nil : srid_provider.find(srs)
|
45
|
+
|
46
|
+
ewkt_builder.from_ewkt(transformer.transform(coordinates, srid: srid, format: format))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Charta
|
4
|
+
module Factory
|
5
|
+
class SimpleGeometryFactory
|
6
|
+
# @return [SimpleFeatureFactory]
|
7
|
+
attr_reader :feature_factory
|
8
|
+
|
9
|
+
# @param [SimpleFeatureFactory] feature_factory
|
10
|
+
def initialize(feature_factory:)
|
11
|
+
@feature_factory = feature_factory
|
12
|
+
end
|
13
|
+
|
14
|
+
def new_geometry(coordinates, srs: nil, format: nil)
|
15
|
+
if coordinates.is_a?(::Charta::Geometry)
|
16
|
+
coordinates
|
17
|
+
else
|
18
|
+
wrap(feature_factory.new_feature(coordinates, srs: srs, format: format))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def empty_geometry(srs)
|
23
|
+
wrap(feature_factory.empty_feature(srs))
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def wrap(feature)
|
29
|
+
case feature.geometry_type
|
30
|
+
when RGeo::Feature::Point
|
31
|
+
Point.new(feature)
|
32
|
+
when RGeo::Feature::LineString
|
33
|
+
LineString.new(feature)
|
34
|
+
when RGeo::Feature::Polygon
|
35
|
+
Polygon.new(feature)
|
36
|
+
when RGeo::Feature::MultiPolygon
|
37
|
+
MultiPolygon.new(feature)
|
38
|
+
when RGeo::Feature::GeometryCollection
|
39
|
+
GeometryCollection.new(feature)
|
40
|
+
else
|
41
|
+
Geometry.new(feature)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -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
@@ -7,12 +7,14 @@ module Charta
|
|
7
7
|
|
8
8
|
def initialize(data, srid = :WGS84)
|
9
9
|
srid ||= :WGS84
|
10
|
-
@json =
|
10
|
+
@json = Coordinates.flatten(data.is_a?(Hash) ? data : JSON.parse(data))
|
11
11
|
lsrid = @json['crs']['properties']['name'] if @json.is_a?(Hash) &&
|
12
12
|
@json['crs'].is_a?(Hash) &&
|
13
13
|
@json['crs']['properties'].is_a?(Hash)
|
14
14
|
lsrid ||= srid
|
15
15
|
@srid = ::Charta.find_srid(lsrid)
|
16
|
+
|
17
|
+
@json = Coordinates.normalize_4326_geometry(@json) if @srid.to_i == 4326
|
16
18
|
end
|
17
19
|
|
18
20
|
def geom
|
@@ -24,7 +26,7 @@ module Charta
|
|
24
26
|
end
|
25
27
|
|
26
28
|
def to_ewkt
|
27
|
-
"SRID=#{
|
29
|
+
"SRID=#{srid};" + EwktSerializer.object_to_ewkt(@json)
|
28
30
|
end
|
29
31
|
|
30
32
|
def valid?
|
@@ -42,131 +44,17 @@ module Charta
|
|
42
44
|
false
|
43
45
|
end
|
44
46
|
|
45
|
-
# Force coordinates to 2D
|
46
47
|
def flatten(hash)
|
47
|
-
|
48
|
-
flatten_feature_collection(hash)
|
49
|
-
elsif hash['type'] == 'Feature'
|
50
|
-
flatten_feature(hash)
|
51
|
-
else
|
52
|
-
flatten_geometry(hash)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def flatten_feature_collection(hash)
|
57
|
-
hash.merge('features' => hash['features'].map { |f| flatten_feature(f) })
|
58
|
-
end
|
59
|
-
|
60
|
-
def flatten_feature(hash)
|
61
|
-
hash.merge('geometry' => flatten_geometry(hash['geometry']))
|
62
|
-
end
|
63
|
-
|
64
|
-
def flatten_geometry(hash)
|
65
|
-
coordinates = hash['coordinates']
|
66
|
-
flattened =
|
67
|
-
case hash['type']
|
68
|
-
when 'Point' then
|
69
|
-
flatten_position(coordinates)
|
70
|
-
when 'MultiPoint', 'LineString'
|
71
|
-
coordinates.map { |p| flatten_position(p) }
|
72
|
-
when 'MultiLineString', 'Polygon'
|
73
|
-
coordinates.map { |l| l.map { |p| flatten_position(p) } }
|
74
|
-
when 'MultiPolygon'
|
75
|
-
coordinates.map { |m| m.map { |l| l.map { |p| flatten_position(p) } } }
|
76
|
-
when 'GeometryCollection' then
|
77
|
-
return hash.merge('geometries' => hash['geometries'].map { |g| flatten_geometry(g) })
|
78
|
-
else
|
79
|
-
raise StandardError, "Cannot handle: #{hash['type'].inspect}. In #{hash.inspect}"
|
80
|
-
end
|
81
|
-
|
82
|
-
hash.merge('coordinates' => flattened)
|
83
|
-
end
|
84
|
-
|
85
|
-
def flatten_position(position)
|
86
|
-
position[0..1]
|
87
|
-
end
|
88
|
-
|
89
|
-
def object_to_ewkt(hash)
|
90
|
-
type = hash[:type] || hash['type']
|
91
|
-
send("#{type.gsub(/(.)([A-Z])/, '\1_\2').downcase}_to_ewkt", hash)
|
92
|
-
end
|
93
|
-
|
94
|
-
def feature_collection_to_ewkt(hash)
|
95
|
-
return 'GEOMETRYCOLLECTION EMPTY' if hash['features'].nil?
|
96
|
-
'GEOMETRYCOLLECTION(' + hash['features'].collect do |feature|
|
97
|
-
object_to_ewkt(feature)
|
98
|
-
end.join(', ') + ')'
|
99
|
-
end
|
100
|
-
|
101
|
-
def geometry_collection_to_ewkt(hash)
|
102
|
-
return 'GEOMETRYCOLLECTION EMPTY' if hash['geometries'].nil?
|
103
|
-
'GEOMETRYCOLLECTION(' + hash['geometries'].collect do |feature|
|
104
|
-
object_to_ewkt(feature)
|
105
|
-
end.join(', ') + ')'
|
106
|
-
end
|
107
|
-
|
108
|
-
def feature_to_ewkt(hash)
|
109
|
-
object_to_ewkt(hash['geometry'])
|
110
|
-
end
|
111
|
-
|
112
|
-
def point_to_ewkt(hash)
|
113
|
-
return 'POINT EMPTY' if hash['coordinates'].nil?
|
114
|
-
'POINT(' + hash['coordinates'].join(' ') + ')'
|
115
|
-
end
|
116
|
-
|
117
|
-
def line_string_to_ewkt(hash)
|
118
|
-
return 'LINESTRING EMPTY' if hash['coordinates'].nil?
|
119
|
-
'LINESTRING(' + hash['coordinates'].collect do |point|
|
120
|
-
point.join(' ')
|
121
|
-
end.join(', ') + ')'
|
122
|
-
end
|
123
|
-
|
124
|
-
def polygon_to_ewkt(hash)
|
125
|
-
return 'POLYGON EMPTY' if hash['coordinates'].nil?
|
126
|
-
'POLYGON(' + hash['coordinates'].collect do |hole|
|
127
|
-
'(' + hole.collect do |point|
|
128
|
-
point.join(' ')
|
129
|
-
end.join(', ') + ')'
|
130
|
-
end.join(', ') + ')'
|
131
|
-
end
|
132
|
-
|
133
|
-
def multi_point_to_ewkt(hash)
|
134
|
-
return 'MULTIPOINT EMPTY' if hash['coordinates'].nil?
|
135
|
-
'MULTIPOINT(' + hash['coordinates'].collect do |point|
|
136
|
-
'(' + point.join(' ') + ')'
|
137
|
-
end.join(', ') + ')'
|
138
|
-
end
|
139
|
-
|
140
|
-
def multi_line_string_to_ewkt(hash)
|
141
|
-
return 'MULTILINESTRING EMPTY' if hash['coordinates'].nil?
|
142
|
-
'MULTILINESTRING(' + hash['coordinates'].collect do |line|
|
143
|
-
'(' + line.collect do |point|
|
144
|
-
point.join(' ')
|
145
|
-
end.join(', ') + ')'
|
146
|
-
end.join(', ') + ')'
|
147
|
-
end
|
148
|
-
|
149
|
-
def multipolygon_to_ewkt(hash)
|
150
|
-
return 'MULTIPOLYGON EMPTY' if hash['coordinates'].nil?
|
151
|
-
'MULTIPOLYGON(' + hash['coordinates'].collect do |polygon|
|
152
|
-
'(' + polygon.collect do |hole|
|
153
|
-
'(' + hole.collect do |point|
|
154
|
-
point.join(' ')
|
155
|
-
end.join(', ') + ')'
|
156
|
-
end.join(', ') + ')'
|
157
|
-
end.join(', ') + ')'
|
48
|
+
Coordinates.flatten hash
|
158
49
|
end
|
159
50
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
end.join(', ') + ')'
|
168
|
-
end.join(', ') + ')'
|
169
|
-
end.join(', ') + ')'
|
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|
|
55
|
+
define_method m do |*args|
|
56
|
+
EwktSerializer.send m, *args
|
57
|
+
end
|
170
58
|
end
|
171
59
|
end
|
172
60
|
end
|