class Terragona::ConcaveHull

Public Class Methods

new(options = {}) click to toggle source
# File lib/terragona/concave_hull.rb, line 7
def initialize(options = {})
  @projection = options[:projection] || 4326
  @table  = options[:table] || 'concave_hull'
  @target_percent = options[:target_percent] || 0.8
  @allow_holes = options[:allow_holes]
  @allow_holes = false if @allow_holes.nil?
  @max_distance_ratio = options[:max_distance_ratio] || 1.6
  @force_homogeneity = options[:force_homogeneity]

  db_options={
      :database=> options[:db_name],
      :user=> options[:db_username],
      :password=> options[:db_password],
      :host=> options[:db_host] || 'localhost',
      :port=> options[:db_port] || 5432,
      :max_connections=> options[:db_max_connections] || 10
  }

  @db = Sequel.postgres(db_options)
  @all_means = []
  create_table
end

Public Instance Methods

perform(points,tags,id) click to toggle source
# File lib/terragona/concave_hull.rb, line 30
def perform(points,tags,id)
  filtered_points=filter_points_by_distance(points)
  points_stringified=stringify_points(filtered_points)

  query = %Q{
  ST_ConcaveHull(
    ST_GeomFromText(
      'MULTIPOINT(
        #{points_stringified}
      )',
      #{@projection}
    ),
    #{@target_percent},
    #{@allow_holes})
  }

  create_concave_hull(query,tags,filtered_points.count,id)
end

Private Instance Methods

clean_str(str) click to toggle source
# File lib/terragona/concave_hull.rb, line 96
def clean_str(str)
  str.to_s.gsub("'",' ')
end
create_concave_hull(query, tags, count, id) click to toggle source
# File lib/terragona/concave_hull.rb, line 57
def create_concave_hull(query, tags, count, id)
  begin
    return unless @db["SELECT ST_GeometryType(#{query})"].first[:st_geometrytype] == 'ST_Polygon'
    @db << "INSERT INTO #{@table} (id, name, count, geometry) VALUES (#{id},'#{clean_str tags}',#{count},#{query})"
  rescue
    puts "Error with #{tags}, Id: #{id}, #{count} points."
  end
end
create_table() click to toggle source
# File lib/terragona/concave_hull.rb, line 51
def create_table
  @db << "DROP TABLE IF EXISTS #{@table};"
  @db << "CREATE TABLE #{@table} (id BIGINT PRIMARY KEY, name TEXT, count INT);"
  @db << "SELECT AddGeometryColumn('#{@table}', 'geometry', #{@projection}, 'POLYGON', 2);"
end
filter_points_by_distance(points) click to toggle source
# File lib/terragona/concave_hull.rb, line 66
def filter_points_by_distance(points)
  random_points = points.count > 200 ? (0..200).map {|e|
    points[rand(points.count)]
  }.uniq : points

  distances = points.map {|p0|
    sum = random_points.map {|pf|
      if p0[:y] != pf[:y] and p0[:x] != pf[:x]
        Geokit::LatLng.new(p0[:y],p0[:x]).distance_to(Geokit::LatLng.new(pf[:y],pf[:x]))
      end
    }.compact
    {:y=>p0[:y],:x=>p0[:x],:mean=> sum.mean}
  }

  mean = distances.map{|d| d[:mean]}.compact.mean
  @all_means.push mean
  mean_of_means =  @force_homogeneity ? @all_means.compact.mean : nil

  distances.map {|d|
    next unless d[:mean]
    d if (d[:mean]/[mean,mean_of_means].compact.min) < @max_distance_ratio
  }.compact
end
stringify_points(points) click to toggle source
# File lib/terragona/concave_hull.rb, line 90
def stringify_points(points)
  points.map{|p|
    "#{p[:x]} #{p[:y]}"
  }.join(',')
end