Class: Charta::Geometry

Inherits:
Object
  • Object
show all
Defined in:
lib/charta/geometry.rb

Overview

Represents a Geometry with SRID

Direct Known Subclasses

GeometryCollection, LineString, Point, Polygon

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(feature, properties = {}) ⇒ Geometry

Returns a new instance of Geometry.



10
11
12
13
# File 'lib/charta/geometry.rb', line 10

def initialize(feature, properties = {})
  self.feature = feature
  @properties = properties
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object



312
313
314
315
316
317
318
319
# File 'lib/charta/geometry.rb', line 312

def method_missing(name, *args, &block)
  target = to_rgeo
  if target.respond_to? name
    target.send name, *args
  else
    raise StandardError.new("Method #{name} does not exist for #{self.class.name}")
  end
end

Class Method Details

.factory(srid = 4326) ⇒ Object



332
333
334
335
336
# File 'lib/charta/geometry.rb', line 332

def factory(srid = 4326)
  return projected_factory(srid) if srid.to_i == 4326

  geos_factory(srid)
end

.feature(ewkt_or_rgeo) ⇒ Object



338
339
340
341
342
# File 'lib/charta/geometry.rb', line 338

def feature(ewkt_or_rgeo)
  return from_rgeo(ewkt_or_rgeo) if ewkt_or_rgeo.is_a? RGeo::Feature::Instance

  from_ewkt(ewkt_or_rgeo)
end

.from_ewkt(ewkt) ⇒ Object



349
350
351
352
353
354
355
356
357
358
359
# File 'lib/charta/geometry.rb', line 349

def from_ewkt(ewkt)
  # Cleans empty geometries
  ewkt = ewkt.gsub(/(GEOMETRYCOLLECTION|GEOMETRY|((MULTI)?(POINT|LINESTRING|POLYGON)))\(\)/, '\1 EMPTY')
  srs = ewkt.split(/[\=\;]+/)[0..1]
  srid = nil
  srid = srs[1] if srs[0] =~ /srid/i
  srid ||= 4326
  factory(srid).parse_wkt(ewkt)
rescue RGeo::Error::ParseError => e
  raise "Invalid EWKT (#{e.class.name}: #{e.message}): #{ewkt}"
end

.from_rgeo(rgeo) ⇒ Object



344
345
346
347
# File 'lib/charta/geometry.rb', line 344

def from_rgeo(rgeo)
  srid = rgeo.srid
  RGeo::Feature.cast(rgeo, factory: Geometry.factory(srid))
end

.srs_databaseObject



328
329
330
# File 'lib/charta/geometry.rb', line 328

def srs_database
  @srs_database ||= RGeo::CoordSys::SRSDatabase::Proj4Data.new('epsg', authority: 'EPSG', cache: true)
end

Instance Method Details

#!=(other) ⇒ Object

Test if the other measure is equal to self



126
127
128
129
130
131
132
# File 'lib/charta/geometry.rb', line 126

def !=(other)
  other_geometry = Charta.new_geometry(other).transform(srid)
  return true if empty? && other_geometry.empty?
  return inspect == other_geometry.inspect if collection? && other_geometry.collection?

  !feature.equals?(other_geometry.feature)
end

#==(other) ⇒ Object

Test if the other measure is equal to self



117
118
119
120
121
122
123
# File 'lib/charta/geometry.rb', line 117

def ==(other)
  other_geometry = Charta.new_geometry(other).transform(srid)
  return true if empty? && other_geometry.empty?
  return inspect == other_geometry.inspect if collection? && other_geometry.collection?

  feature.equals?(other_geometry.feature)
end

#areaObject

Returns area in unit corresponding to the SRS



144
145
146
147
148
149
150
151
152
153
154
# File 'lib/charta/geometry.rb', line 144

def area
  if surface?
    if collection?
      feature.sum { |geometry| Charta.new_geometry(geometry).area }
    else
      feature.area
    end
  else
    0
  end
end

#bounding_boxObject



265
266
267
268
269
270
271
272
273
274
275
# File 'lib/charta/geometry.rb', line 265

def bounding_box
  unless defined? @bounding_box
    bbox = RGeo::Cartesian::BoundingBox.create_from_geometry(feature)
    instance_variable_set('@x_min', bbox.min_x || 0)
    instance_variable_set('@y_min', bbox.min_y || 0)
    instance_variable_set('@x_max', bbox.max_x || 0)
    instance_variable_set('@y_max', bbox.max_y || 0)
    @bounding_box = BoundingBox.new(@y_min, @x_min, @y_max, @x_max)
  end
  @bounding_box
end

#buffer(radius) ⇒ Object

Produces buffer



237
238
239
# File 'lib/charta/geometry.rb', line 237

def buffer(radius)
  feature.buffer(radius)
end

#centroidObject

Computes the geometric center of a geometry, or equivalently, the center of mass of the geometry as a POINT.



166
167
168
169
170
171
# File 'lib/charta/geometry.rb', line 166

def centroid
  return nil unless surface? && !feature.is_empty?

  point = feature.centroid
  [point.y, point.x]
end

#collection?Boolean

Returns the type of the geometry as a string. EG: ‘ST_Linestring’, ‘ST_Polygon’, ‘ST_MultiPolygon’ etc. This function differs from GeometryType(geometry) in the case of the string and ST in front that is returned, as well as the fact that it will not indicate whether the geometry is measured.

Returns:

  • (Boolean)


29
30
31
# File 'lib/charta/geometry.rb', line 29

def collection?
  feature.geometry_type == RGeo::Feature::GeometryCollection
end

#convert_to(new_type) ⇒ Object



181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/charta/geometry.rb', line 181

def convert_to(new_type)
  case new_type
  when type
    self
  when :multi_point
    flatten_multi(:point)
  when :multi_line_string
    flatten_multi(:line_string)
  when :multi_polygon
    flatten_multi(:polygon)
  else
    self
  end
end

#difference(other) ⇒ Object Also known as: -



258
259
260
261
# File 'lib/charta/geometry.rb', line 258

def difference(other)
  other_geometry = Charta.new_geometry(other).transform(srid)
  feature.difference(other_geometry.feature)
end

#empty?Boolean Also known as: blank?

Returns true if this Geometry is an empty geometrycollection, polygon, point etc.

Returns:

  • (Boolean)


158
159
160
# File 'lib/charta/geometry.rb', line 158

def empty?
  feature.is_empty?
end

#ewktObject



54
55
56
57
# File 'lib/charta/geometry.rb', line 54

def ewkt
  puts 'DEPRECATION WARNING: Charta::Geometry.ewkt is deprecated. Please use Charta::Geometry.to_ewkt instead'
  to_ewkt
end

#featureObject

Returns the underlaying object managed by Charta: the RGeo feature



288
289
290
291
292
293
294
295
296
297
298
# File 'lib/charta/geometry.rb', line 288

def feature
  unless defined? @feature
    if defined? @ewkt
      @feature = ::Charta::Geometry.from_ewkt(@ewkt)
      @properties = @options.dup if @options
    else
      raise StandardError.new('Invalid geometry (no feature, no EWKT)')
    end
  end
  @feature.dup
end

#feature=(new_feature) ⇒ Object

Raises:

  • (ArgumentError)


302
303
304
305
306
# File 'lib/charta/geometry.rb', line 302

def feature=(new_feature)
  raise ArgumentError.new("Feature can't be nil") if new_feature.nil?

  @feature = new_feature
end

#find_srid(name_or_srid) ⇒ Object



283
284
285
# File 'lib/charta/geometry.rb', line 283

def find_srid(name_or_srid)
  Charta.find_srid(name_or_srid)
end

#flatten_multi(as_type) ⇒ Object



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/charta/geometry.rb', line 196

def flatten_multi(as_type)
  items = []
  as_multi_type = "multi_#{as_type}".to_sym
  if type == as_type
    items << feature
  elsif type == :geometry_collection
    feature.each do |geom|
      type_name = Charta.underscore(geom.geometry_type.type_name).to_sym
      if type_name == as_type
        items << geom
      elsif type_name == as_multi_type
        geom.each do |item|
          items << item
        end
      end
    end
  end
  Charta.new_geometry(feature.factory.send(as_multi_type, items))
end

#inspectObject



15
16
17
# File 'lib/charta/geometry.rb', line 15

def inspect
  "<#{self.class.name}(#{to_ewkt})>"
end

#intersection(other) ⇒ Object



248
249
250
251
# File 'lib/charta/geometry.rb', line 248

def intersection(other)
  other_geometry = Charta.new_geometry(other).transform(srid)
  feature.intersection(other_geometry.feature)
end

#intersects?(other) ⇒ Boolean

Returns:

  • (Boolean)


253
254
255
256
# File 'lib/charta/geometry.rb', line 253

def intersects?(other)
  other_geometry = Charta.new_geometry(other).transform(srid)
  feature.intersects?(other_geometry.feature)
end

#merge(other) ⇒ Object Also known as: +



241
242
243
244
# File 'lib/charta/geometry.rb', line 241

def merge(other)
  other_geometry = Charta.new_geometry(other).transform(srid)
  feature.union(other_geometry.feature)
end

#point_on_surfaceObject

Returns a POINT guaranteed to lie on the surface.



174
175
176
177
178
179
# File 'lib/charta/geometry.rb', line 174

def point_on_surface
  return nil unless surface?

  point = feature.point_on_surface
  [point.y, point.x]
end

#respond_to_missing?(name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


321
322
323
324
325
# File 'lib/charta/geometry.rb', line 321

def respond_to_missing?(name, include_private = false)
  return false if name == :init_with

  super
end

#sridObject

Return the spatial reference identifier for the ST_Geometry



34
35
36
# File 'lib/charta/geometry.rb', line 34

def srid
  feature.srid.to_i
end

#surface?Boolean

Returns true if Geometry is a Surface

Returns:

  • (Boolean)


135
136
137
138
139
140
141
# File 'lib/charta/geometry.rb', line 135

def surface?
  if collection?
    feature.any? { |geometry| Charta.new_geometry(geometry).surface? }
  else
    [RGeo::Feature::Polygon, RGeo::Feature::MultiPolygon].include? feature.geometry_type
  end
end

#to_binaryObject Also known as: to_ewkb

Return the Well-Known Binary (WKB) representation of the geometry with SRID meta data.



60
61
62
63
# File 'lib/charta/geometry.rb', line 60

def to_binary
  generator = RGeo::WKRep::WKBGenerator.new(tag_format: :ewkbt, emit_ewkbt_srid: true)
  generator.generate(feature)
end

#to_ewktObject Also known as: to_s

Returns EWKT: WKT with its SRID



48
49
50
# File 'lib/charta/geometry.rb', line 48

def to_ewkt
  Charta.generate_ewkt(feature).to_s
end

#to_geojsonObject Also known as: to_json

Return the geometry as a Geometry Javascript Object Notation (GeoJSON) element.



105
106
107
# File 'lib/charta/geometry.rb', line 105

def to_geojson
  to_json_object.to_json
end

#to_json_feature(properties = {}) ⇒ Object



308
309
310
# File 'lib/charta/geometry.rb', line 308

def to_json_feature(properties = {})
  { type: 'Feature', properties: properties, geometry: to_json_object }
end

#to_json_objectObject

Returns object in JSON (Hash)



112
113
114
# File 'lib/charta/geometry.rb', line 112

def to_json_object
  RGeo::GeoJSON.encode(feature)
end

#to_rgeoObject

Returns the underlaying object managed by Charta: the RGeo feature



300
301
302
303
304
305
306
307
308
309
310
# File 'lib/charta/geometry.rb', line 300

def feature
  unless defined? @feature
    if defined? @ewkt
      @feature = ::Charta::Geometry.from_ewkt(@ewkt)
      @properties = @options.dup if @options
    else
      raise StandardError.new('Invalid geometry (no feature, no EWKT)')
    end
  end
  @feature.dup
end

#to_svg(options = {}) ⇒ String

Generate SVG from geometry

Parameters:

  • options (Hash) (defaults to: {})

    , the options for SVG object.

Options Hash (options):

  • :mode (Hash)

    could be :stroke or :fill

  • :color (Hash)

    could be “orange”, “red”, “blue” or HTML color “#14TF15”

  • :fill_opacity (Hash)

    could be ‘0’ to ‘100’

  • :stroke_linecap (Hash)

    could be ‘round’, ‘square’, ‘butt’

  • :stroke_linejoin (Hash)

    default ‘round’

  • :stroke_width (Hash)

    default ‘5%’

Returns:

  • (String)

    the SVG image



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/charta/geometry.rb', line 77

def to_svg(options = {})
  # set default options if not present
  options[:mode] ||= :stroke
  options[:color] ||= 'black'
  options[:fill_opacity] ||= '100' # 0 to 100
  options[:stroke_linecap] ||= 'butt' # round, square, butt
  options[:stroke_linejoin] ||= 'round' #
  options[:stroke_width] ||= '5%'

  svg = Victor::SVG.new template: :html
  svg.setup width: 180, height: 180, viewBox: bounding_box.svg_view_box.join(' ')
  # return a stroke SVG with options
  if options[:mode] == :stroke
    svg.path d: to_svg_path, fill: 'none', stroke: options[:color], stroke_linecap: options[:stroke_linecap],
stroke_linejoin: options[:stroke_linejoin], stroke_width: options[:stroke_width]
  # return a fill SVG with options
  elsif options[:mode] == :fill
    svg.path d: to_svg_path, fill: options[:color], fill_opacity: options[:fill_opacity]
  end
  svg.render
end

#to_svg_pathObject

Return the geometry as Scalar Vector Graphics (SVG) path data.



100
101
102
# File 'lib/charta/geometry.rb', line 100

def to_svg_path
  RGeo::SVG.encode(feature)
end

#to_textObject Also known as: as_text, to_wkt

Returns the Well-Known Text (WKT) representation of the geometry/geography without SRID metadata



40
41
42
# File 'lib/charta/geometry.rb', line 40

def to_text
  feature.as_text.match(/\ASRID=.*;(.*)/)[1]
end

#transform(new_srid) ⇒ Object

Returns a new geometry with the coordinates converted into the new SRS



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/charta/geometry.rb', line 217

def transform(new_srid)
  return self if new_srid == srid
  raise 'Proj is not supported. Cannot tranform' unless RGeo::CoordSys::Proj4.supported?

  new_srid = Charta::SRS[new_srid] || new_srid
  database = self.class.srs_database
  new_proj_entry = database.get(new_srid)
  raise "Cannot find proj for SRID: #{new_srid}" if new_proj_entry.nil?

  new_feature = RGeo::CoordSys::Proj4.transform(
    database.get(srid).proj4,
    feature,
    new_proj_entry.proj4,
    self.class.factory(new_srid)
  )
  generator = RGeo::WKRep::WKTGenerator.new(tag_format: :ewkt, emit_ewkt_srid: true)
  Charta.new_geometry(generator.generate(new_feature))
end

#typeObject

Returns the type of the geometry as a string. Example: point, multi_polygon, geometry_collection…



21
22
23
# File 'lib/charta/geometry.rb', line 21

def type
  Charta.underscore(feature.geometry_type.type_name).to_sym
end