Class: GeographicItem

Inherits:
ActiveRecord::Base
  • Object
show all
Includes:
Housekeeping::Users
Defined in:
app/models/geographic_item.rb

Constant Summary collapse

DATA_TYPES =

include ActiveRecordSpatial::SpatialColumns

include ActiveRecordSpatial::SpatialScopes
self.create_spatial_column_accessors! # except: ['point']
[:point,
:line_string,
:polygon,
:multi_point,
:multi_line_string,
:multi_polygon,
:geometry_collection]

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Housekeeping::Users

#alive?, #set_created_by_id, #set_updated_by_id

Class Method Details

.clean!Object



199
200
201
202
203
204
205
206
# File 'app/models/geographic_item.rb', line 199

def self.clean!
  # given the list of orphan shapes (in 't20140306'), delete them, drop the table, and return the list (which is
  # probably not very useful).
  list = clean?
  GeographicItem.connection.execute('delete from geographic_items where id in (select id from t20140306)')
  GeographicItem.connection.execute('drop table t20140306')
  list
end

.clean?Boolean

Returns:

  • (Boolean)


182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'app/models/geographic_item.rb', line 182

def self.clean?
  # There may be cases where there are orphan shape, since the table for this model in NOT normalized for shape.
  # This method is provided to find all of the orphans, and store their ids in the table 't20140306', and return an
  # array to the caller.
  GeographicItem.connection.execute('DROP TABLE IF EXISTS t20140306')
  GeographicItem.connection.execute('CREATE TABLE t20140306(id integer)')
  GeographicItem.connection.execute('delete from t20140306')
  GeographicItem.connection.execute('insert into t20140306 select id from geographic_items')

  GeographicItem.connection.execute('delete from t20140306 where id in (select ne_geo_item_id from geographic_areas where ne_geo_item_id is not null)')
  GeographicItem.connection.execute('delete from t20140306 where id in (select tdwg_geo_item_id from geographic_areas where tdwg_geo_item_id is not null)')
  GeographicItem.connection.execute('delete from t20140306 where id in (select gadm_geo_item_id from geographic_areas where gadm_geo_item_id is not null)')
  GeographicItem.connection.execute('delete from t20140306 where id in (select geographic_item_id from georeferences where geographic_item_id is not null)')

  list = GeographicItem.connection.execute('select id from t20140306').to_a
end

.containing(column_name, *geographic_items) ⇒ Object

If this method is given an Array of GeographicItems as a second parameter, it will return the 'or' of each of the objects against the table



124
125
126
127
# File 'app/models/geographic_item.rb', line 124

def self.containing(column_name, *geographic_items)
  # where{"ST_contains(#{column_name}::geometry, ST_GeomFromText('srid=4326;POINT(-29 -16)'))"}
  where { geographic_items.flatten.collect { |geographic_item| GeographicItem.containing_sql(column_name, geographic_item) }.join(' or ') }
end

.containing_sql(column_name, geographic_item) ⇒ Object



157
158
159
160
161
162
163
# File 'app/models/geographic_item.rb', line 157

def self.containing_sql(column_name, geographic_item)
  # where{"ST_contains(#{column_name}::geometry, ST_GeomFromText('srid=4326;POINT(-29 -16)'))"}
  # was ST_GeomFromText
  check_geo_params(column_name, geographic_item) ?
    "ST_Contains(#{column_name}::geometry, GeomFromEWKT('srid=4326;#{geographic_item.geo_object}'))" :
    'false'
end

.contains?(geo_object_a, geo_object_b) ⇒ Boolean

scope :intersecting_boxes, -> (column_name, geographic_item)

  select("ST_Contains(geographic_items.#{column_name, #geographic_itemgeographic_item.geo_object)",
  ) }

def self.same(geo_object_a, geo_object_b)
  # http://postgis.refractions.net/documentation/manual-1.4/ST_Geometry_Same.html
  # boolean ~=( geometry A , geometry B );
  # TODO: not sure how to specify '~=', syntactically substituting 'same'
  where(st_geometry_same(geo_object_a, geo_object_b))
  # returns true if the two objects are, vertex-by-vertex, the same
end

def self.area(geo_polygon) # or multi_polygon
  # http://postgis.refractions.net/documentation/manual-1.4/ST_Area.html
  # float ST_Area(geometry g1);
  where(st_area(geo_polygon))
  # returns the area area of the geometry as a float
end

def self.azimuth(geo_point_a, geo_point_b)
  # http://postgis.refractions.net/documentation/manual-1.4/ST_Azimuth.html
  # float ST_Azimuth(geometry pointA, geometry pointB);
  where(st_azimuth(geo_point_a, geo_point_b))
end

def self.centroid(geo_object)
  # http://postgis.refractions.net/documentation/manual-1.4/ST_Centroid.html
  # geometry ST_Centroid(geometry g1);
  where(st_centroid(geo_object))
  # returns a point
end

def self.find_containing(column_name, geo_object)
  # ST_Contains(geometry, geometry) or
  # ST_Contains(geography, geography)
  wheregeo_object)
end

Returns:

  • (Boolean)


88
89
90
91
92
# File 'app/models/geographic_item.rb', line 88

def self.contains?(geo_object_a, geo_object_b)
  # ST_Contains(geometry, geometry) or
  # ST_Contains(geography, geography)
  where { st_contains(st_geomfromewkb(geo_object_a), st_geomfromewkb(geo_object_b)) }
end

.disjoint_from(column_name, *geographic_items) ⇒ Object



117
118
119
120
# File 'app/models/geographic_item.rb', line 117

def self.disjoint_from(column_name, *geographic_items)
  # select id from geographic_items where st_disjoint( polygon::geometry, GeomFromEWKT('srid=4326;POLYGON ((-19.0 9.0 0.0, -9.0 9.0 0.0, -9.0 2.0 0.0, -19.0 2.0 0.0, -19.0 9.0 0.0))') ) = true ;
  where { geographic_items.flatten.collect { |geographic_item| "st_disjoint(#{column_name}::geometry, GeomFromEWKT('srid=4326;#{geographic_item.geo_object}'))" }.join(' and ') }
end

.excluding(geographic_items) ⇒ Object



177
178
179
180
# File 'app/models/geographic_item.rb', line 177

def self.excluding(geographic_items)
  # where{ geographic_items.flatten.collect { |geographic_item| "id != #{geographic_item.id}" }.join(' and ')}
  where.not(id: geographic_items)
end

.intersecting(column_name, *geographic_items) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'app/models/geographic_item.rb', line 94

def self.intersecting(column_name, *geographic_items)

  where { geographic_items.flatten.collect { |geographic_item| "ST_Intersects(#{column_name}, 'srid=4326;#{geographic_item.geo_object}')" }.join(' and ') }

=begin
  geographic_items.each { |geographic_item|
    # where("st_contains(geographic_items.#{column_name}, ST_GeomFromText('#{geographic_item.to_s}'))")
    # TODO: see if st_intersects, instead of st_contains makes a difference
    where {"st_intersects(#{column_name}, #{geographic_item.geo_object})"}
    #where { st_intersects(st_geomfromewkb("geographic_items.#{column_name}"), geographic_item.geo_object.as_binary) }
    # where("st_contains(geographic_items.#{column_name}, ST_GeomFromText('#{geographic_item.to_s}'))")
  }
=end
end

.ordered_by_longest_distance_from(column_name, geographic_item) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
# File 'app/models/geographic_item.rb', line 145

def self.ordered_by_longest_distance_from(column_name, geographic_item)
  # select id, st_distance(point, geomfromewkt('srid=4326;POINT(-28 -21)')) as distance from geographic_items where point is not null order by delta limit 4;
  if check_geo_params(column_name, geographic_item)
    f = select { '*' }.
      select_distance(column_name, geographic_item).
      where_distance_greater_than_zero(column_name,geographic_item).
      order { 'distance desc' }
  else
    where { 'false' }
  end
end

.ordered_by_shortest_distance_from(column_name, geographic_item) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'app/models/geographic_item.rb', line 129

def self.ordered_by_shortest_distance_from(column_name, geographic_item)
  # select id, st_distance(point, geomfromewkt('srid=4326;POINT(-28 -21)')) as distance from geographic_items where point is not null order by delta limit 4;
  # select { "geographic_items.*, ST_Distance(#{column_name}, GeomFromEWKT('srid=4326;#{geographic_item.geo_object}')) as distance" }.where { "#{column_name} is not null and ST_Distance(#{column_name}, GeomFromEWKT('srid=4326;#{geographic_item.geo_object}')) > 0" }.order{'distance'} :

  if check_geo_params(column_name, geographic_item)
    #"ST_Distance(#{column_name}::geometry, GeomFromEWKT('srid=4326;#{geographic_item.geo_object}'))"
    f = select { '*' }.
      select_distance(column_name, geographic_item).
      where_distance_greater_than_zero(column_name, geographic_item).
      order { 'distance' }
  else
    where { 'false' }
  end

end

.select_distance(column_name, geographic_item) ⇒ Object



165
166
167
# File 'app/models/geographic_item.rb', line 165

def self.select_distance(column_name, geographic_item)
  select { "ST_Distance(#{column_name}, GeomFromEWKT('srid=4326;#{geographic_item.geo_object}')) as distance" }
end

.select_distance_with_geo_object(column_name, geographic_item) ⇒ Object



169
170
171
# File 'app/models/geographic_item.rb', line 169

def self.select_distance_with_geo_object(column_name, geographic_item)
  select{'*'}.select_distance(column_name, geographic_item)
end

.where_distance_greater_than_zero(column_name, geographic_item) ⇒ Object



173
174
175
# File 'app/models/geographic_item.rb', line 173

def self.where_distance_greater_than_zero(column_name,geographic_item)
  where { "#{column_name} is not null and ST_Distance(#{column_name}, GeomFromEWKT('srid=4326;#{geographic_item.geo_object}')) > 0" }
end

.within_radius_of(column_name, geographic_item, distance) ⇒ Object



109
110
111
112
113
114
115
# File 'app/models/geographic_item.rb', line 109

def self.within_radius_of(column_name, geographic_item, distance)
  if check_geo_params(column_name, geographic_item)
    where {"st_distance(#{column_name}, GeomFromEWKT('srid=4326;#{geographic_item.geo_object}')) < #{distance}"}
  else
    where { 'false' }
  end
end

Instance Method Details

#contains?(item) ⇒ Boolean

Returns:

  • (Boolean)


217
218
219
# File 'app/models/geographic_item.rb', line 217

def contains?(item)
  self.geo_object.contains?(item.geo_object)
end

#data_type?Boolean

Returns:

  • (Boolean)


237
238
239
# File 'app/models/geographic_item.rb', line 237

def data_type?
  DATA_TYPES.each { |item| return item if !self.send(item).nil? }
end

#distance?(item) ⇒ Boolean

Returns:

  • (Boolean)


225
226
227
# File 'app/models/geographic_item.rb', line 225

def distance?(item)
  self.geo_object.distance?(item)
end

#far(item, distance) ⇒ Object



233
234
235
# File 'app/models/geographic_item.rb', line 233

def far(item, distance)
  !self.near(item, distance)
end

#geo_objectObject

return false if the record has not been saved, or if there are no geographic objects in the record.



208
209
210
211
212
213
214
215
# File 'app/models/geographic_item.rb', line 208

def geo_object # return false if the record has not been saved, or if there are no geographic objects in the record.
  return false if self.new_record?
  DATA_TYPES.each do |t|
    # otherwise, return the first-found object, according to the list of DATA_TYPES
    return self.send(t) if !self.send(t).nil?
  end
  false
end

#near(item, distance) ⇒ Object



229
230
231
# File 'app/models/geographic_item.rb', line 229

def near(item, distance)
  self.geo_object.buffer(distance).contains?(item.geo_object)
end

#rendering_hashObject

Return the geo_object as a set of points with object type as key like: {

points:  [],
lines:   [],
polygons: []
}


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
# File 'app/models/geographic_item.rb', line 247

def rendering_hash
  data = {}
  if self.geo_object
    case self.data_type?
      when :point
        data = point_to_hash(self.point)
      when :line_string
        data = line_string_to_hash(self.line_string)
      when :polygon
        data = polygon_to_hash(self.polygon)
      when :multi_point
        data = multi_point_to_hash(self.multi_point)
      when :multi_line_string
        data = multi_line_string_to_hash(self.multi_line_string)
      when :multi_polygon
        data = multi_polygon_to_hash(self.multi_polygon)
      when :geometry_collection
        data = self.geometry_collection_to_hash(self.geometry_collection)
      else
        # do nothing
    end
  end

  data.delete_if { |key, value| value == [] } # remove any keys with empty arrays
  data
end

#to_aObject



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'app/models/geographic_item.rb', line 278

def to_a
  # get our object and return the set of points as an array
  we_are = self.geo_object

  if we_are
    data = []
    case self.data_type?
      when :point
        data = point_to_a(self.point)
      when :line_string
        data = line_string_to_a(self.line_string)
      when :polygon
        data = polygon_to_a(self.polygon)
      when :multi_point
        data = multi_point_to_a(self.multi_point)
      when :multi_line_string
        data = multi_line_string_to_a(self.multi_line_string)
      when :multi_polygon
        data = multi_polygon_to_a(self.multi_polygon)
      #when :geometry_collection
      #  data = geometry_collection_to_a(self.geometry_collection)
      else
        # do nothing
    end
    data
  else
    []
  end
end

#to_geo_jsonObject



274
275
276
# File 'app/models/geographic_item.rb', line 274

def to_geo_json
  RGeo::GeoJSON.encode(self.geo_object).to_json
end

#within?(item) ⇒ Boolean

Returns:

  • (Boolean)


221
222
223
# File 'app/models/geographic_item.rb', line 221

def within?(item)
  self.geo_object.within?(item.geo_object)
end