Permalink
Browse files

Initial

  • Loading branch information...
0 parents commit 88bbf5206b8810105d8c81c4c0fa4c93a1e1ea97 @joshuaclayton committed Sep 5, 2011
@@ -0,0 +1,4 @@
+*.gem
+.bundle
+Gemfile.lock
+pkg/*
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in douglas_peucker.gemspec
+gemspec
@@ -0,0 +1,22 @@
+Copyright (c) 2011 Josh Clayton
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,24 @@
+# Douglas-Peucker Algorithm
+
+This algorithm simplifies a line by recursively dividing a line and removing points outside of a predefined threshold. Check out the [Wikipedia page](http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm) to read up on it more.
+
+## Usage
+
+ >> require "douglas_peucker"
+ => true
+ >> line = [[0, 0], [1, 1], [2, 1], [3, 2.5], [4, 4]]
+ => [[0, 0], [1, 1], [2, 1], [3, 2.5], [4, 4]]
+ >> DouglasPeucker::LineSimplifier.new(line).threshold(1).points
+ => [[0, 0], [4, 4]]
+ >> DouglasPeucker::LineSimplifier.new(line).threshold(0.5).points
+ => [[0, 0], [2, 1], [4, 4]]
+ >> DouglasPeucker::LineSimplifier.new(line).points
+ => [[0, 0], [1, 1], [2, 1], [4, 4]]
+
+## License
+
+See the LICENSE
+
+## Author
+
+Written by Josh Clayton, although the algorithm and its various implementations have been written before.
@@ -0,0 +1,7 @@
+require "bundler"
+Bundler::GemHelper.install_tasks
+
+require "rspec/core/rake_task"
+RSpec::Core::RakeTask.new(:rspec)
+
+task :default => [:rspec]
@@ -0,0 +1,21 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+require "douglas_peucker/version"
+
+Gem::Specification.new do |s|
+ s.name = "douglas_peucker"
+ s.version = DouglasPeucker::VERSION
+ s.platform = Gem::Platform::RUBY
+ s.authors = ["Josh Clayton"]
+ s.email = ["joshua.clayton@gmail.com"]
+ s.homepage = ""
+ s.summary = %q{A Ruby implementation of the Douglas–Peucker algorithm}
+ s.summary = %q{A Ruby implementation of the Douglas–Peucker algorithm}
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
+ s.require_paths = ["lib"]
+
+ s.add_development_dependency("rspec", "~> 2.6.0")
+end
@@ -0,0 +1,7 @@
+module DouglasPeucker
+ autoload :Line, "douglas_peucker/line"
+ autoload :Point, "douglas_peucker/point"
+ autoload :OrthogonalDistance, "douglas_peucker/orthogonal_distance"
+ autoload :LineSimplifier, "douglas_peucker/line_simplifier"
+ autoload :Version, "douglas_peucker/version"
+end
@@ -0,0 +1,3 @@
+module DouglasPeucker
+ class Line < Struct.new(:start, :end); end
+end
@@ -0,0 +1,50 @@
+module DouglasPeucker
+ class LineSimplifier
+
+ def initialize(points)
+ @points = points
+ @threshold = 10e-8
+ end
+
+ def threshold(threshold)
+ @threshold = threshold
+ self
+ end
+
+ def points
+ calculate_points(@points)
+ end
+
+ private
+
+ def orthogonal_distance(point, line_start, line_end)
+ line = Line.new(Point.new(line_start[0], line_start[1]), Point.new(line_end[0], line_end[1]))
+ point = Point.new(point[0], point[1])
+ OrthogonalDistance.new(point, line).distance
+ end
+
+ def calculate_points(points)
+ return points if points.length < 3
+
+ maximum_distance = 0
+ index = 0
+
+ (1..(points.length - 1)).each do |i|
+ current_distance = orthogonal_distance(points[i], points.first, points.last)
+ if current_distance > maximum_distance
+ index = i
+ maximum_distance = current_distance
+ end
+ end
+
+ if maximum_distance >= @threshold
+ results_1 = calculate_points(points[0..index])
+ results_2 = calculate_points(points[index..-1])
+
+ results_1[0..-2] + results_2
+ else
+ [points.first, points.last]
+ end
+ end
+ end
+end
@@ -0,0 +1,18 @@
+module DouglasPeucker
+ # From http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
+ class OrthogonalDistance < Struct.new(:point, :line)
+ def distance
+ numerator.abs/denominator**0.5
+ end
+
+ private
+
+ def numerator
+ ((line.end.x - line.start.x)*(line.start.y - point.y) - (line.start.x - point.x)*(line.end.y - line.start.y))
+ end
+
+ def denominator
+ (line.end.x - line.start.x)**2 + (line.end.y - line.start.y)**2
+ end
+ end
+end
@@ -0,0 +1,3 @@
+module DouglasPeucker
+ class Point < Struct.new(:x, :y); end
+end
@@ -0,0 +1,3 @@
+module DouglasPeucker
+ VERSION = "0.0.1"
+end
Oops, something went wrong.

0 comments on commit 88bbf52

Please sign in to comment.