Skip to content

Commit

Permalink
Geographic items refactoring experiments.
Browse files Browse the repository at this point in the history
  • Loading branch information
mjy committed Jul 3, 2014
1 parent 5e38d5d commit b3d27f6
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 17 deletions.
63 changes: 48 additions & 15 deletions app/models/geographic_item.rb
Expand Up @@ -94,10 +94,6 @@ def st_npoints
GeographicItem.where(id: self.id).select("ST_NPoints(#{self.st_as_binary}) number_points").first['number_points'].to_i
end

def st_as_binary
"ST_AsBinary(#{self.geo_object_type})"
end

def parent_geographic_areas
self.geographic_areas.collect { |a| a.parent }
end
Expand Down Expand Up @@ -161,13 +157,22 @@ def center_coords
st_centroid
end

def st_as_binary
"ST_AsBinary(#{self.geo_object_type})"
end

# TODO: Find ST_Centroid(g1) method and
# Return an Array of [latitude, longitude] for the centroid of GeoItem
def st_centroid
# GeographicItem.where(id: self.id).select("ST_NPoints(#{self.st_as_binary}) number_points").first['number_points'].to_i
GeographicItem.where(id: self.id).select("st_astext(st_centroid(st_geomfromewkb(#{self.st_as_binary})")
GeographicItem.where(id: self.id).select( "id, st_astext(st_centroid( #{to_geometry_sql} )) centroid" ).first
end

def to_geometry_sql
"ST_GeomFromEWKB( #{self.geo_object_type} )"
end


=begin
scope :intersecting_boxes, -> (column_name, geographic_item) {
select("ST_Contains(geographic_items.#{column_name}, #{geographic_item.geo_object})",
Expand Down Expand Up @@ -240,13 +245,14 @@ def intersecting(column_name, *geographic_items)

else
q = geographic_items.flatten.collect { |geographic_item|
"ST_Intersects(#{column_name}, 'srid=4326;#{geographic_item.geo_object}')" # seems like we want this: http://danshultz.github.io/talks/mastering_activerecord_arel/#/15/2
"ST_Intersects(#{column_name}, '#{geographic_item.geo_object}' )" # seems like we want this: http://danshultz.github.io/talks/mastering_activerecord_arel/#/15/2
}.join(' or ')

where(q)
end

end
end # end class << self


def st_intersects(column_name = :multi_polygon, geometry)
geographic_item = GeographicItem.arel_table
Expand All @@ -264,25 +270,52 @@ def st_intersects(column_name = :multi_polygon, geometry)
=end
end

# TODO: Document, what units are distance in?
# todo: distance is measured in meters
def self.within_radius_of(column_name, geographic_item, distance)

# Trying with Arel
# setup an Arel table
# append conditions with each loop to the table
# project the result
# geographic_items = GeographicItem.arel_table
# conditions = []
# st_distances = []

# g1 = geographic_items.alias
# g1[:id].eq(geographic_item.id).project(column)

# DATA_TYPES.each do |column|
#
# g2 = geographic_items.where(geographic_items[:id].eq(geographic_item.id))
# g3 = g2[column]

# a = Arel::Nodes::NamedFunction.new("st_distance", [ geographic_items[column.to_sym], g2 ] )
# byebug
# conditions.push(where(a.lt(distance)))
# end


# distance is measured in meters
def self.within_radius_of(column_name, geographic_item, distance) # ultimately it should be geographic_item_id
if column_name.downcase == 'any'
partial = []

DATA_TYPES.each { |column|
partial.push(GeographicItem.within_radius_of("#{column}", geographic_item, distance).to_a)
partial.push(GeographicItem.within_radius_of("#{column}", geographic_item, distance))
}
# todo: change 'id in (?)' to some other sql construct
GeographicItem.where(id: partial.flatten.map(&:id))

GeographicItem.where(id: partial.flatten.map(&:id) )
else
if check_geo_params(column_name, geographic_item)
where ("st_distance(#{column_name}, GeomFromEWKT('srid=4326;#{geographic_item.geo_object}')) < #{distance}")
where("st_distance(#{column_name}, #{ select_geography_sql(geographic_item) }) < #{distance}")
else
where ('false')
where("false")
end
end
end

def self.select_geography_sql(geographic_item)
"(SELECT #{geographic_item.geo_object_type} from geographic_items where id = #{geographic_item.to_param})"
end

def self.disjoint_from(column_name, *geographic_items)
q = geographic_items.flatten.collect { |geographic_item|
"st_disjoint(#{column_name}::geometry, GeomFromEWKT('srid=4326;#{geographic_item.geo_object}'))"
Expand Down
2 changes: 1 addition & 1 deletion spec/models/collecting_event_spec.rb
Expand Up @@ -293,7 +293,7 @@
# The idea:
# - geopolitical names all come from GeographicArea, as classified by GeographicAreaType
# - we can arrive at a geographic_area from a collecting event in 2 ways
# 1) @collecting_event.geographic_area is set, this is easy
# 1) @collecting_event.geographic_area is set, this is easy, we can use it specifically if it's the right type, or climb up to a specific levelN category to check if not
# 2) @collecting_event.georeferences.first is set.
# In the case of 2) we must use the georeference to find the minimum containing geographic_area of type "state" for example
# - it is possible (but hopefully unlikely) that multiple geographic areas of type "state" might be return,
Expand Down
6 changes: 5 additions & 1 deletion spec/models/geographic_item_spec.rb
Expand Up @@ -589,7 +589,7 @@

specify '#st_centroid returns a lat/lng of the centroid of the GeoObject' do
# select st_centroid('multipoint (-4.0 4.0 0.0, 4.0 4.0 0.0, 4.0 -4.0 0.0, -4.0 -4.0 0.0)');
expect(@area_d.st_centroid).to eq([1.0, 0.0])
expect(@area_d.st_centroid['centroid'].to_s).to eq('POINT (0.0 0.0 0.0)')
end

specify '#start_point returns a lat/lng of the first point of the GeoObject' do
Expand Down Expand Up @@ -772,6 +772,10 @@
expect(GeographicItem.within_radius_of('polygon', @p0, 1000000)).to eq([@e2, @e3, @e4, @e5, @area_a, @area_b, @area_c, @area_d])
end

skip '::within_radius("any"...)' do
# expect(GeographicItem.within_radius_of('any', @p0, 1000000)).to eq([@e2, @e3, @e4, @e5, @area_a, @area_b, @area_c, @area_d])
end

specify "::intersecting list of objects (uses 'or')" do
expect(GeographicItem.intersecting('polygon', [@l])).to eq([@k])
expect(GeographicItem.intersecting('polygon', [@f1])).to eq([]) # Is this right?
Expand Down

0 comments on commit b3d27f6

Please sign in to comment.