Module: RGeo::Feature

Defined in:
lib/rgeo/feature.rb,
lib/rgeo/feature/line.rb,
lib/rgeo/feature/curve.rb,
lib/rgeo/feature/point.rb,
lib/rgeo/feature/types.rb,
lib/rgeo/feature/mixins.rb,
lib/rgeo/feature/factory.rb,
lib/rgeo/feature/polygon.rb,
lib/rgeo/feature/surface.rb,
lib/rgeo/feature/geometry.rb,
lib/rgeo/feature/line_string.rb,
lib/rgeo/feature/linear_ring.rb,
lib/rgeo/feature/multi_curve.rb,
lib/rgeo/feature/multi_point.rb,
lib/rgeo/feature/multi_polygon.rb,
lib/rgeo/feature/multi_surface.rb,
lib/rgeo/feature/factory_generator.rb,
lib/rgeo/feature/multi_line_string.rb,
lib/rgeo/feature/geometry_collection.rb

Overview

The Feature namespace contains interfaces and general tools for implementations of the Open Geospatial Consortium Simple Features Specification (SFS), version 1.1.0.

Each interface is defined as a module, and is provided primarily for the sake of documentation. Implementations do not necessarily include the modules themselves. Therefore, you should not depend on the kind_of? method to check type. Instead, each interface module will provide a check_type class method (and a corresponding === operator to support case-when constructs).

In addition, a Factory interface is defined here. A factory is an object that knows how to construct geometry instances for a given implementation. Each implementation’s front-end consists of a way to create factories. Those factories, in turn, provide the api for building the features themselves. Note that, like the geometry modules, the Factory module itself may not actually be included in a factory implementation.

Any particular implementation may extend these interfaces to provide implementation-specific features beyond what is stated in the SFS itself. The implementation should separately document any such extensions that it may provide.

Defined Under Namespace

Modules: Curve, Factory, FactoryGenerator, Geometry, GeometryCollection, Instance, Line, LineString, LinearRing, MultiCurve, MultiLineString, MultiPoint, MultiPolygon, MultiSurface, Point, Polygon, Surface, Type Classes: MixinCollection

Class Method Summary collapse

Class Method Details

.cast(obj_, *params_) ⇒ Object

Cast the given object according to the given parameters, if possible, and return the resulting object. If the requested cast is not possible, nil is returned.

Parameters may be provided as a hash, or as separate arguments. Hash keys are as follows:

:factory

Set the factory to the given factory. If this argument is not given, the original object’s factory is kept.

:type

Cast to the given type, which must be a module in the RGeo::Feature namespace. If this argument is not given, the result keeps the same type as the original.

:project

If this is set to true, and both the original and new factories support proj4 projections, then the cast will also cause the coordinates to be transformed between those two projections. If set to false, the coordinates are not modified. Default is false.

:keep_subtype

Value must be a boolean indicating whether to keep the subtype of the original. If set to false, casting to a particular type always casts strictly to that type, even if the old type is a subtype of the new type. If set to true, the cast retains the subtype in that case. For example, casting a LinearRing to a LineString will normally yield a LineString, even though LinearRing is already a more specific subtype. If you set this value to true, the casted object will remain a LinearRing. Default is false.

:force_new

Always return a newly-created object, even if neither the type nor factory is modified. Normally, if this is set to false, and a cast is not set to modify either the factory or type, the original object itself is returned. Setting this flag to true causes cast to return a clone in that case. Default is false.

You may also pass the new factory, the new type, and the flags as separate arguments. In this case, the flag names must be passed as symbols, and their effect is the same as setting their values to true. You can even combine separate arguments and hash arguments. For example, the following three calls are equivalent:

RGeo::Feature.cast(geom, :type => RGeo::Feature::Point, :project => true)
RGeo::Feature.cast(geom, RGeo::Feature::Point, :project => true)
RGeo::Feature.cast(geom, RGeo::Feature::Point, :project)

RGeo provides a default casting algorithm. Individual feature implementation factories may override this and customize the casting behavior by defining the override_cast method. See ::RGeo::Feature::Factory#override_cast for more details.



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/rgeo/feature/types.rb', line 161

def cast(obj_, *params_)
  # Interpret params
  factory_ = obj_.factory
  type_ = obj_.geometry_type
  opts_ = {}
  params_.each do |param_|
    case param_
    when Factory::Instance
      opts_[:factory] = param_
    when Type
      opts_[:type] = param_
    when ::Symbol
      opts_[param_] = true
    when ::Hash
      opts_.merge!(param_)
    end
  end
  force_new_ = opts_[:force_new]
  keep_subtype_ = opts_[:keep_subtype]
  project_ = opts_[:project]
  nfactory_ = opts_.delete(:factory) || factory_
  ntype_ = opts_.delete(:type) || type_

  # Let the factory override
  if nfactory_.respond_to?(:override_cast)
    override_ = nfactory_.override_cast(obj_, ntype_, opts_)
    return override_ unless override_ == false
  end

  # Default algorithm
  ntype_ = type_ if keep_subtype_ && type_.include?(ntype_)
  if ntype_ == type_
    # Types are the same
    if nfactory_ == factory_
      force_new_ ? obj_.dup : obj_
    else
      if type_ == Point
        proj_ = nproj_ = nil
        if project_
          proj_ = factory_.proj4
          nproj_ = nfactory_.proj4
        end
        hasz_ = factory_.property(:has_z_coordinate)
        nhasz_ = nfactory_.property(:has_z_coordinate)
        if proj_ && nproj_
          coords_ = CoordSys::Proj4.transform_coords(proj_, nproj_, obj_.x, obj_.y, hasz_ ? obj_.z : nil)
          coords_ << (hasz_ ? obj_.z : 0.0) if nhasz_ && coords_.size < 3
        else
          coords_ = [obj_.x, obj_.y]
          coords_ << (hasz_ ? obj_.z : 0.0) if nhasz_
        end
        coords_ << (factory_.property(:has_m_coordinate) ? obj_.m : 0.0) if nfactory_.property(:has_m_coordinate)
        nfactory_.point(*coords_)
      elsif type_ == Line
        nfactory_.line(cast(obj_.start_point, nfactory_, opts_), cast(obj_.end_point, nfactory_, opts_))
      elsif type_ == LinearRing
        nfactory_.linear_ring(obj_.points.map { |p_| cast(p_, nfactory_, opts_) })
      elsif type_ == LineString
        nfactory_.line_string(obj_.points.map { |p_| cast(p_, nfactory_, opts_) })
      elsif type_ == Polygon
        nfactory_.polygon(cast(obj_.exterior_ring, nfactory_, opts_),
                          obj_.interior_rings.map { |r_| cast(r_, nfactory_, opts_) })
      elsif type_ == MultiPoint
        nfactory_.multi_point(obj_.map { |g_| cast(g_, nfactory_, opts_) })
      elsif type_ == MultiLineString
        nfactory_.multi_line_string(obj_.map { |g_| cast(g_, nfactory_, opts_) })
      elsif type_ == MultiPolygon
        nfactory_.multi_polygon(obj_.map { |g_| cast(g_, nfactory_, opts_) })
      elsif type_ == GeometryCollection
        nfactory_.collection(obj_.map { |g_| cast(g_, nfactory_, opts_) })
      end
    end
  else
    # Types are different
    if ntype_ == Point && (type_ == MultiPoint || type_ == GeometryCollection) ||
        (ntype_ == Line || ntype_ == LineString || ntype_ == LinearRing) && (type_ == MultiLineString || type_ == GeometryCollection) ||
        ntype_ == Polygon && (type_ == MultiPolygon || type_ == GeometryCollection)
      if obj_.num_geometries == 1
        cast(obj_.geometry_n(0), nfactory_, ntype_, opts_)
      end
    elsif ntype_ == Point
      nil
    elsif ntype_ == Line
      if type_ == LineString && obj_.num_points == 2
        nfactory_.line(cast(obj_.point_n(0), nfactory_, opts_), cast(obj_.point_n(1), nfactory_, opts_))
      end
    elsif ntype_ == LinearRing
      if type_ == LineString
        nfactory_.linear_ring(obj_.points.map { |p_| cast(p_, nfactory_, opts_) })
      end
    elsif ntype_ == LineString
      if type_ == Line || type_ == LinearRing
        nfactory_.line_string(obj_.points.map { |p_| cast(p_, nfactory_, opts_) })
      end
    elsif ntype_ == MultiPoint
      if type_ == Point
        nfactory_.multi_point([cast(obj_, nfactory_, opts_)])
      elsif type_ == GeometryCollection
        nfactory_.multi_point(obj_.map { |p_| cast(p_, nfactory_, opts_) })
      end
    elsif ntype_ == MultiLineString
      if type_ == Line || type_ == LinearRing || type_ == LineString
        nfactory_.multi_line_string([cast(obj_, nfactory_, opts_)])
      elsif type_ == GeometryCollection
        nfactory_.multi_line_string(obj_.map { |p_| cast(p_, nfactory_, opts_) })
      end
    elsif ntype_ == MultiPolygon
      if type_ == Polygon
        nfactory_.multi_polygon([cast(obj_, nfactory_, opts_)])
      elsif type_ == GeometryCollection
        nfactory_.multi_polygon(obj_.map { |p_| cast(p_, nfactory_, opts_) })
      end
    elsif ntype_ == GeometryCollection
      if type_ == MultiPoint || type_ == MultiLineString || type_ == MultiPolygon
        nfactory_.collection(obj_.map { |p_| cast(p_, nfactory_, opts_) })
      else
        nfactory_.collection([cast(obj_, nfactory_, opts_)])
      end
    end
  end
end