rgeo-kml 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 06227b992bdd5a2c5169650431585db1648c8b04
4
+ data.tar.gz: df4e6c62b71585076ddafef8721e85f770b4f871
5
+ SHA512:
6
+ metadata.gz: 4fb3bb15dd469492f7dda89d9a6141441531e9afa19f3166ba40d253a690ad1b56df2fb212d11276cf3a7979bd1fce0831653bd9f16d42ae1225e0e3a4804c3d
7
+ data.tar.gz: 786a6e34ea432784590336be029e4e466ea47ad8d3644701ce2693155953edfcd2a1afb4daf6581525ec9f4577d70fe097230377db22f71fb2f26b6e0533bfba
data/Version ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,7 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Kml implementation for RGeo
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+
7
+ require 'rgeo/kml'
@@ -0,0 +1,39 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Kml implementation for RGeo
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+
7
+ # Dependencies
8
+ require 'rgeo'
9
+
10
+ # RGeo is a spatial data library for Ruby, provided by the "rgeo" gem.
11
+ #
12
+ # The optional RGeo::GeoJSON module provides a set of tools for GeoJSON
13
+ # encoding and decoding.
14
+
15
+ module RGeo
16
+
17
+
18
+ # This is a namespace for a set of tools that provide KML encoding.
19
+ # See https://developers.google.com/kml/documentation/ for more information about this specification.
20
+
21
+ module Kml
22
+ end
23
+
24
+
25
+ end
26
+
27
+
28
+ # Implementation files
29
+ require 'rgeo/kml/version'
30
+ require 'rgeo/kml/entities'
31
+ require 'rgeo/kml/coder'
32
+ require 'rgeo/kml/interface'
33
+ require 'rgeo/kml/kml_stream_listener'
34
+ require 'rgeo/kml/coordinates_builder'
35
+ require 'rgeo/kml/point_builder'
36
+ require 'rgeo/kml/line_string_builder'
37
+ require 'rgeo/kml/linear_ring_builder'
38
+ require 'rgeo/kml/polygon_builder'
39
+ require 'rgeo/kml/multi_geometry_builder'
@@ -0,0 +1,222 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Kml encoder object
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+
7
+ require "rexml/document"
8
+ require 'rexml/parsers/pullparser'
9
+
10
+ module RGeo
11
+
12
+ module Kml
13
+
14
+
15
+ # This object encapsulates encoding and decoding settings (principally
16
+ # the RGeo::Feature::Factory and the RGeo::Kml::EntityFactory to
17
+ # be used) so that you can encode and decode without specifying those
18
+ # settings every time.
19
+
20
+ class Coder
21
+
22
+ ELEMENT_MAP = {
23
+ 'Point' => "point",
24
+ 'LineString' => "line_string",
25
+ 'LinearRing' => "linear_ring",
26
+ 'Polygon' => "polygon",
27
+ 'MultiGeometry' => "geometry_collection"
28
+ }
29
+
30
+ @@kml_available = nil
31
+ @@activesupport_available = nil
32
+
33
+
34
+ # Create a new coder settings object. The geo factory is passed as
35
+ # a required argument.
36
+ #
37
+ # Options include:
38
+ #
39
+ # [<tt>:geo_factory</tt>]
40
+ # Specifies the geo factory to use to create geometry objects.
41
+ # Defaults to the preferred cartesian factory.
42
+ # [<tt>:entity_factory</tt>]
43
+ # Specifies an entity factory, which lets you override the types
44
+ # of Kml entities that are created. It defaults to the default
45
+ # RGeo::Kml::EntityFactory, which generates objects of type
46
+ # RGeo::Kml::Feature or RGeo::Kml::FeatureCollection.
47
+ # See RGeo::Kml::EntityFactory for more information.
48
+
49
+ def initialize(opts_={})
50
+ @geo_factory = opts_[:geo_factory] || ::RGeo::Cartesian.preferred_factory
51
+ @entity_factory = opts_[:entity_factory] || EntityFactory.instance
52
+ @buffer = ''
53
+
54
+ @num_coordinates = 2
55
+ @num_coordinates += 1 if @geo_factory.property(:has_z_coordinate)
56
+ @num_coordinates += 1 if @geo_factory.property(:has_m_coordinate)
57
+ end
58
+
59
+
60
+ # Encode the given object as Kml. The object may be one of the
61
+ # geometry objects specified in RGeo::Feature, or an appropriate
62
+ # Kml wrapper entity supported by this coder's entity factory.
63
+ #
64
+ # This method returns a KML object (xml with Placemark).
65
+ #
66
+ # Returns nil if nil is passed in as the object.
67
+
68
+ def encode(object_)
69
+ if @entity_factory.is_feature_collection?(object_)
70
+ {
71
+ 'type' => 'FeatureCollection',
72
+ 'features' => @entity_factory.map_feature_collection(object_){ |f_| _encode_feature(f_) },
73
+ }
74
+ elsif @entity_factory.is_feature?(object_)
75
+ _encode_feature(object_)
76
+ elsif object_.nil?
77
+ nil
78
+ else
79
+ _encode_geometry(object_)
80
+ end
81
+ end
82
+
83
+
84
+ # Returns the RGeo::Feature::Factory used to generate geometry objects.
85
+
86
+ def geo_factory
87
+ @geo_factory
88
+ end
89
+
90
+
91
+ # Returns the RGeo::Kml::EntityFactory used to generate Kml
92
+ # wrapper entities.
93
+
94
+ def entity_factory
95
+ @entity_factory
96
+ end
97
+
98
+
99
+ def _encode_feature(object_) # :nodoc:
100
+ id_ = @entity_factory.get_feature_id(object_)
101
+ kml_ = '<?xml version="1.0" encoding="UTF-8"?>\n'
102
+ kml_ += '<kml xmlns="http://www.opengis.net/kml/2.2">\n'
103
+ kml_ += "<Document>"
104
+ kml_ += "<name>Kml File</name>"
105
+ kml_ += "<Placemark id="#{id_}">\n"
106
+ kml_ += _encode_geometry(@entity_factory.get_feature_geometry(object_))
107
+ kml_ += "</Placemark>\n"
108
+ kml_ += "</Document>"
109
+ kml_ += "</kml>"
110
+
111
+ # 'properties' => @entity_factory.get_feature_properties(object_).dup,
112
+ # id_ = @entity_factory.get_feature_id(object_)
113
+ # json_['id'] = id_ if id_
114
+ kml_
115
+ end
116
+
117
+
118
+ def _encode_geometry(object_, point_encoder_=nil) # :nodoc:
119
+ unless point_encoder_
120
+ if object_.factory.property(:has_z_coordinate)
121
+ if object_.factory.property(:has_m_coordinate)
122
+ point_encoder_ = ::Proc.new{ |p_| [p_.x, p_.y, p_.z, p_.m].join(",") }
123
+ else
124
+ point_encoder_ = ::Proc.new{ |p_| [p_.x, p_.y, p_.z].join(",") }
125
+ end
126
+ else
127
+ if object_.factory.property(:has_m_coordinate)
128
+ point_encoder_ = ::Proc.new{ |p_| [p_.x, p_.y, p_.m].join(",") }
129
+ else
130
+ point_encoder_ = ::Proc.new{ |p_| [p_.x, p_.y].join(",") }
131
+ end
132
+ end
133
+ end
134
+ case object_
135
+ when ::RGeo::Feature::Point
136
+ result = "<Point>\n"
137
+ #result += options[:geom_data] if options[:geom_data]
138
+ result += "<coordinates>" + point_encoder_.call(object_)
139
+ result += "</coordinates>\n"
140
+ result += "</Point>\n"
141
+ when ::RGeo::Feature::LineString
142
+ result = "<LineString>\n"
143
+ #result += options[:geom_data] if options[:geom_data]
144
+ result += "<coordinates>\n"
145
+ result += object_.points.map(&point_encoder_).join("\n")
146
+ result += "\n</coordinates>\n"
147
+ result += "</LineString>\n"
148
+ when ::RGeo::Feature::Polygon
149
+ result = "<Polygon>\n"
150
+ #result += options[:geom_data] if options[:geom_data]
151
+ result += "<outerBoundaryIs><LinearRing><coordinates>\n"
152
+ result += object_.exterior_ring.points.map(&point_encoder_).join("\n")
153
+ result += "\n</coordinates></LinearRing></outerBoundaryIs>\n"
154
+ object_.interior_rings.each do |interior_ring|
155
+ result += "<innerBoundaryIs><LinearRing><coordinates>\n"
156
+ result += interior_ring.points.map(&point_encoder_).join("\n")
157
+ result += "\n</coordinates></LinearRing></innerBoundaryIs>\n"
158
+ end
159
+ result += "</Polygon>\n"
160
+ when ::RGeo::Feature::MultiPoint
161
+ result = "<MultiGeometry>\n"
162
+ object_.each do |geometry|
163
+ result += "<Point>\n<coordinates>"
164
+ #options[:id_attr] = "" #the subgeometries do not have an ID
165
+ result += point_encoder_.call(geometry)
166
+ result += "</coordinates>\n</Point>\n"
167
+ end
168
+ result += "</MultiGeometry>\n"
169
+ when ::RGeo::Feature::MultiLineString
170
+ result = "<MultiGeometry>\n"
171
+ object_.each do |geometry|
172
+ #options[:id_attr] = "" #the subgeometries do not have an ID
173
+ result += "<LineString>\n"
174
+ #result += options[:geom_data] if options[:geom_data]
175
+ result += "<coordinates>\n"
176
+ result += geometry.points.map(&point_encoder_).join("\n")
177
+ result += "\n</coordinates>\n"
178
+ result += "</LineString>\n"
179
+ end
180
+ result += "</MultiGeometry>\n"
181
+ when ::RGeo::Feature::MultiPolygon
182
+ result = "<MultiGeometry>\n"
183
+ object_.each do |geometry|
184
+ #options[:id_attr] = "" #the subgeometries do not have an ID
185
+ result += "<Polygon>\n"
186
+ #result += options[:geom_data] if options[:geom_data]
187
+ result += "<outerBoundaryIs><LinearRing><coordinates>\n"
188
+ result += geometry.exterior_ring.points.map(&point_encoder_).join("\n")
189
+ result += "\n</coordinates></LinearRing></outerBoundaryIs>\n"
190
+ geometry.interior_rings.each do |interior_ring|
191
+ result += "<innerBoundaryIs><LinearRing><coordinates>\n"
192
+ result += interior_ring.points.map(&point_encoder_).join("\n")
193
+ result += "\n</coordinates></LinearRing></innerBoundaryIs>\n"
194
+ end
195
+ result += "</Polygon>\n"
196
+ end
197
+ result += "</MultiGeometry>\n"
198
+ when ::RGeo::Feature::GeometryCollection
199
+ result = "<MultiGeometry>\n"
200
+ #options[:id_attr] = "" #the subgeometries do not have an ID
201
+ result += object_.map{ |geom_| _encode_geometry(geom_, point_encoder_) }.join("")
202
+ result += "</MultiGeometry>\n"
203
+ else
204
+ nil
205
+ end
206
+ end
207
+
208
+
209
+ # Decode an object from Kml. The input may a
210
+ # String, or an IO object from which to read the KML string.
211
+ # If an error occurs, nil is returned.
212
+ def decode(input_)
213
+ @kml_stream_listener = KmlStreamListener.new(geo_factory)
214
+ @kml_stream_listener.parse(input_)
215
+ @kml_stream_listener.result
216
+ end
217
+
218
+ end
219
+
220
+ end
221
+
222
+ end
@@ -0,0 +1,37 @@
1
+ module RGeo
2
+ module Kml
3
+
4
+ class CoordinatesBuilder
5
+ attr_reader :geo_factory, :parent, :points
6
+ attr_accessor :text
7
+
8
+ def initialize( geo_factory, parent)
9
+ @geo_factory = geo_factory
10
+ @parent = parent
11
+ @points = []
12
+ end
13
+
14
+ def build
15
+ @text.gsub(/\n/, ' ').strip.split(/\s+/).each do |coord|
16
+ x, y, z = coord.split(',')
17
+ if x.nil? || y.nil?
18
+ fail StandardError, 'Coordinates must have at least x and y elements'
19
+ end
20
+ if z.nil?
21
+ @points << @geo_factory.point(x, y)
22
+ else
23
+ @points << @geo_factory.point(x, y, z)
24
+ end
25
+
26
+ @points
27
+ end
28
+
29
+ rescue Exception => e
30
+ puts "Exception #{e.message} \n #{e.backtrace}"
31
+ raise StandardError, 'Error parsing coordinates: check your kml for errors'
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,275 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Kml standard entities
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+
7
+
8
+ module RGeo
9
+
10
+ module Kml
11
+
12
+
13
+ # This is a Kml wrapper entity that corresponds to the Kml
14
+ # "Feature" type. It is an immutable type.
15
+ #
16
+ # This is the default implementation that is generated by
17
+ # RGeo::Kml::EntityFactory. You may replace this implementation
18
+ # by writing your own entity factory. Note that an alternate Feature
19
+ # implementation need not subclass or even duck-type this class.
20
+ # the entity factory mediates all interaction between the Kml
21
+ # engine and features.
22
+
23
+ class Feature
24
+
25
+
26
+ # Create a feature wrapping the given geometry, with the given ID
27
+ # and properties.
28
+
29
+ def initialize(geometry_, id_=nil, properties_={})
30
+ @geometry = geometry_
31
+ @id = id_
32
+ @properties = {}
33
+ properties_.each do |k_, v_|
34
+ @properties[k_.to_s] = v_
35
+ end
36
+ end
37
+
38
+
39
+ def inspect # :nodoc:
40
+ "#<#{self.class}:0x#{object_id.to_s(16)} id=#{@id.inspect} geom=#{@geometry ? @geometry.as_text.inspect : 'nil'}>"
41
+ end
42
+
43
+ def to_s # :nodoc:
44
+ inspect
45
+ end
46
+
47
+ def hash # :nodoc:
48
+ @geometry.hash + @id.hash + @properties.hash
49
+ end
50
+
51
+
52
+ # Two features are equal if their geometries, IDs, and properties
53
+ # are all equal.
54
+ # This method uses the eql? method to test geometry equality, which
55
+ # may behave differently than the == operator.
56
+
57
+ def eql?(rhs_)
58
+ rhs_.kind_of?(Feature) && @geometry.eql?(rhs_.geometry) && @id.eql?(rhs_.feature_id) && @properties.eql?(rhs_.instance_variable_get(:@properties))
59
+ end
60
+
61
+
62
+ # Two features are equal if their geometries, IDs, and properties
63
+ # are all equal.
64
+ # This method uses the == operator to test geometry equality, which
65
+ # may behave differently than the eql? method.
66
+
67
+ def ==(rhs_)
68
+ rhs_.kind_of?(Feature) && @geometry == rhs_.geometry && @id == rhs_.feature_id && @properties == rhs_.instance_variable_get(:@properties)
69
+ end
70
+
71
+
72
+ # Returns the geometry contained in this feature, which may be nil.
73
+
74
+ def geometry
75
+ @geometry
76
+ end
77
+
78
+
79
+ # Returns the ID for this feature, which may be nil.
80
+
81
+ def feature_id
82
+ @id
83
+ end
84
+
85
+
86
+ # Returns a copy of the properties for this feature.
87
+
88
+ def properties
89
+ @properties.dup
90
+ end
91
+
92
+
93
+ # Gets the value of the given named property.
94
+ # Returns nil if the given property is not found.
95
+
96
+ def property(key_)
97
+ @properties[key_.to_s]
98
+ end
99
+ alias_method :[], :property
100
+
101
+
102
+ # Gets an array of the known property keys in this feature.
103
+
104
+ def keys
105
+ @properties.keys
106
+ end
107
+
108
+
109
+ end
110
+
111
+
112
+ # This is a Kml wrapper entity that corresponds to the Kml
113
+ # "FeatureCollection" type. It is an immutable type.
114
+ #
115
+ # This is the default implementation that is generated by
116
+ # RGeo::Kml::EntityFactory. You may replace this implementation
117
+ # by writing your own entity factory. Note that an alternate
118
+ # FeatureCollection implementation need not subclass or even
119
+ # duck-type this class. The entity factory mediates all interaction
120
+ # between the Kml engine and feature collections.
121
+
122
+ class FeatureCollection
123
+
124
+ include ::Enumerable
125
+
126
+
127
+ # Create a new FeatureCollection with the given features, which must
128
+ # be provided as an Enumerable.
129
+
130
+ def initialize(features_=[])
131
+ @features = []
132
+ features_.each{ |f_| @features << f_ if f_.kind_of?(Feature) }
133
+ end
134
+
135
+
136
+ def inspect # :nodoc:
137
+ "#<#{self.class}:0x#{object_id.to_s(16)}>"
138
+ end
139
+
140
+ def to_s # :nodoc:
141
+ inspect
142
+ end
143
+
144
+ def hash # :nodoc:
145
+ @features.hash
146
+ end
147
+
148
+
149
+ # Two feature collections are equal if they contain the same
150
+ # features in the same order.
151
+ # This methods uses the eql? method to test geometry equality, which
152
+ # may behave differently than the == operator.
153
+
154
+ def eql?(rhs_)
155
+ rhs_.kind_of?(FeatureCollection) && @features.eql?(rhs_.instance_variable_get(:@features))
156
+ end
157
+
158
+
159
+ # Two feature collections are equal if they contain the same
160
+ # features in the same order.
161
+ # This methods uses the == operator to test geometry equality, which
162
+ # may behave differently than the eql? method.
163
+
164
+ def ==(rhs_)
165
+ rhs_.kind_of?(FeatureCollection) && @features == rhs_.instance_variable_get(:@features)
166
+ end
167
+
168
+
169
+ # Iterates or returns an iterator for the features.
170
+
171
+ def each(&block_)
172
+ @features.each(&block_)
173
+ end
174
+
175
+
176
+ # Returns the number of features contained in this collection.
177
+
178
+ def size
179
+ @features.size
180
+ end
181
+
182
+
183
+ # Access a feature by index.
184
+
185
+ def [](index_)
186
+ @features[index_]
187
+ end
188
+
189
+
190
+ end
191
+
192
+
193
+ # This is the default entity factory. It creates objects of type
194
+ # RGeo::Kml::Feature and RGeo::Kml::FeatureCollection.
195
+ # You may create your own entity factory by duck-typing this class.
196
+
197
+ class EntityFactory
198
+
199
+
200
+ # Create and return a new feature, given geometry, ID, and
201
+ # properties hash. Note that, per the Kml spec, geometry and/or
202
+ # properties may be nil.
203
+
204
+ def feature(geometry_, id_=nil, properties_=nil)
205
+ Feature.new(geometry_, id_, properties_ || {})
206
+ end
207
+
208
+
209
+ # Create and return a new feature collection, given an enumerable
210
+ # of feature objects.
211
+
212
+ def feature_collection(features_=[])
213
+ FeatureCollection.new(features_)
214
+ end
215
+
216
+
217
+ # Returns true if the given object is a feature created by this
218
+ # entity factory.
219
+
220
+ def is_feature?(object_)
221
+ object_.kind_of?(Feature)
222
+ end
223
+
224
+
225
+ # Returns true if the given object is a feature collection created
226
+ # by this entity factory.
227
+
228
+ def is_feature_collection?(object_)
229
+ object_.kind_of?(FeatureCollection)
230
+ end
231
+
232
+
233
+ # Run Enumerable#map on the features contained in the given feature
234
+ # collection.
235
+
236
+ def map_feature_collection(object_, &block_)
237
+ object_.map(&block_)
238
+ end
239
+
240
+
241
+ # Returns the geometry associated with the given feature.
242
+
243
+ def get_feature_geometry(object_)
244
+ object_.geometry
245
+ end
246
+
247
+
248
+ # Returns the ID of the given feature, or nil for no ID.
249
+
250
+ def get_feature_id(object_)
251
+ object_.feature_id
252
+ end
253
+
254
+
255
+ # Returns the properties of the given feature as a hash. Editing
256
+ # this hash does not change the state of the feature.
257
+
258
+ def get_feature_properties(object_)
259
+ object_.properties
260
+ end
261
+
262
+
263
+ # Return the singleton instance of EntityFactory.
264
+
265
+ def self.instance
266
+ @instance ||= self.new
267
+ end
268
+
269
+
270
+ end
271
+
272
+
273
+ end
274
+
275
+ end