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