border_patrol 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *~
2
+ #*
3
+ *.sw*
4
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source :rubygems
2
+
3
+ gem "nokogiri", "= 1.4.3.1"
4
+
5
+ group :development do
6
+ gem "rake", "= 0.8.7"
7
+ gem "rspec", "= 1.3.0"
8
+ gem "progressbar"
9
+ end
10
+
@@ -0,0 +1,31 @@
1
+ ---
2
+ dependencies:
3
+ rake:
4
+ group:
5
+ - :development
6
+ version: = 0.8.7
7
+ rspec:
8
+ group:
9
+ - :development
10
+ version: = 1.3.0
11
+ progressbar:
12
+ group:
13
+ - :development
14
+ version: ">= 0"
15
+ nokogiri:
16
+ group:
17
+ - :default
18
+ version: = 1.4.3.1
19
+ specs:
20
+ - rake:
21
+ version: 0.8.7
22
+ - nokogiri:
23
+ version: 1.4.3.1
24
+ - progressbar:
25
+ version: 0.9.0
26
+ - rspec:
27
+ version: 1.3.0
28
+ hash: cbbadd85a1348941a32f127e7ae81222e0cfb8bc
29
+ sources:
30
+ - Rubygems:
31
+ uri: http://gemcutter.org
@@ -0,0 +1,40 @@
1
+ # BorderPatrol
2
+
3
+ BorderPatrol lets you import a KML file and then check if points are inside or outside the polygons the file defines.
4
+
5
+ The KML file may have multiple polygons defined, google maps is a good source.
6
+
7
+ ## Examples
8
+
9
+ An example KML file can be found here:
10
+ http://maps.google.com/maps/ms?ie=UTF8&hl=en&msa=0&ll=38.814031,-103.743896&spn=9.600749,16.248779&z=7&msid=110523771099674876521.00049301d20252132a92c&output=kml
11
+
12
+ To test if a point is in the region you can either pass a class that responds to `x` and `y` (like the provided BorderPatrol::Point class) or just pass a longitude latitude pair.
13
+
14
+ region = BorderPatrol.parse_kml(File.read('spec/support/colorado-test.kml'))
15
+ denver = BorderPatrol::Point.new(-105, 39.75)
16
+ region.contains_point?(denver) # true
17
+ region.contains_point?(-105, 39.75) # also true!
18
+ san_francisco = BorderPatrol::Point.new(-122.5, 37.75)
19
+ region.contains_point?(san_francisco) # false
20
+ region.contains_point?(-122.5, 37.75) # also false!
21
+
22
+ If you want to use your own point class, just define `x` and `y` as methods that correspond to `longitude` and `latitude`.
23
+
24
+ ## Pro Tip
25
+
26
+ You can make KML files easily on Google Maps by clicking "My Maps", drawing shapes and saving the map. Just copy the share link and add "&output=kml" to download the file.g
27
+
28
+ ## Dependencies
29
+
30
+ * Nokogiri
31
+
32
+ ## Known Issues
33
+
34
+ Polygons across the international date line don't work.
35
+
36
+ ## Acknowledgements
37
+
38
+ http://jakescruggs.blogspot.com/2009/07/point-inside-polygon-in-ruby.html for evaluating the algorithm.
39
+
40
+ http://github.com/nofxx/georuby/ for providing the bounding box code.
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+
5
+ require 'rake'
6
+ require 'spec/rake/spectask'
7
+
8
+
9
+ Spec::Rake::SpecTask.new(:spec) do |spec|
10
+ spec.spec_files = FileList['spec/**/*_spec.rb']
11
+ spec.libs << 'spec'
12
+ spec.spec_opts = ["--options", "spec/spec.opts"]
13
+ end
14
+
15
+ task :default => :spec
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ require 'bundler'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "border_patrol"
7
+ s.version = File.read("lib/VERSION").strip
8
+
9
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
+ s.rubygems_version = "1.3.7"
11
+
12
+ s.authors = ["Zach Brock", "Matt Wilson"]
13
+ s.email = "eng@squareup.com"
14
+
15
+ s.date = "2010-10-20"
16
+ s.description = "Lets you import a KML file and then check if points are inside or outside the polygons the file defines."
17
+ s.summary = "Import and query KML regions"
18
+ s.homepage = "http://github.com/square/border_patrol"
19
+
20
+ s.rdoc_options = ["--charset=UTF-8"]
21
+
22
+ s.require_paths = ["lib"]
23
+ root_files = %w(border_patrol.gemspec Rakefile README.markdown .gitignore Gemfile Gemfile.lock)
24
+ s.files = Dir['{lib,spec}/**/*'] + root_files
25
+ s.test_files = Dir['spec/**/*']
26
+
27
+ s.add_bundler_dependencies
28
+ end
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,28 @@
1
+ module BorderPatrol
2
+ class InsufficientPointsToActuallyFormAPolygonError < ArgumentError; end
3
+ class Point < Struct.new(:x, :y); end
4
+
5
+ def self.parse_kml(string)
6
+ doc = Nokogiri::XML(string)
7
+ polygons = doc.xpath('//kml:Polygon', 'kml' => 'http://earth.google.com/kml/2.2').map do |polygon_kml|
8
+ parse_kml_polygon_data(polygon_kml.to_s)
9
+ end
10
+ BorderPatrol::Region.new(polygons)
11
+ end
12
+
13
+ private
14
+ def self.parse_kml_polygon_data(string)
15
+ doc = Nokogiri::XML(string)
16
+ coordinates = doc.xpath("//coordinates").text.strip.split("\n")
17
+ points = coordinates.map do |coord|
18
+ x, y, z = coord.strip.split(',')
19
+ BorderPatrol::Point.new(x.to_f, y.to_f)
20
+ end
21
+ BorderPatrol::Polygon.new(points)
22
+ end
23
+ end
24
+
25
+ require 'set'
26
+ require 'nokogiri'
27
+ require 'border_patrol/polygon.rb'
28
+ require 'border_patrol/region.rb'
@@ -0,0 +1,73 @@
1
+ module BorderPatrol
2
+ class Polygon < Array
3
+ def initialize(* args)
4
+ args.flatten!
5
+ args.uniq!
6
+ raise InsufficientPointsToActuallyFormAPolygonError unless args.size > 2
7
+ super(args)
8
+ end
9
+
10
+ def ==(other)
11
+ # Do we have the right number of points?
12
+ return false unless other.size == size
13
+
14
+ # Are the points in the right order?
15
+ first, second = self.first(2)
16
+ index = other.index(first)
17
+ return false unless index
18
+ direction = (other[index-1] == second) ? -1 : 1
19
+ # Check if the two polygons have the same edges and the same points
20
+ # i.e. [point1, point2, point3] is the same as [point2, point3, point1] is the same as [point3, point2, point1]
21
+ each do |i|
22
+ return false unless i == other[index]
23
+ index = index + direction
24
+ index = 0 if index == size
25
+ end
26
+ true
27
+ end
28
+
29
+ # Quick and dirty hash function
30
+ def hash
31
+ inject(0) { |sum, point| sum += point.x + point.y }
32
+ end
33
+
34
+ def contains_point?(point)
35
+ return false unless inside_bounding_box?(point)
36
+ c = false
37
+ i = -1
38
+ j = self.size - 1
39
+ while (i += 1) < self.size
40
+ if ((self[i].y <= point.y && point.y < self[j].y) ||
41
+ (self[j].y <= point.y && point.y < self[i].y))
42
+ if (point.x < (self[j].x - self[i].x) * (point.y - self[i].y) /
43
+ (self[j].y - self[i].y) + self[i].x)
44
+ c = !c
45
+ end
46
+ end
47
+ j = i
48
+ end
49
+ return c
50
+ end
51
+
52
+ def inside_bounding_box?(point)
53
+ bb_point_1, bb_point_2 = bounding_box
54
+ max_x = [bb_point_1.x, bb_point_2.x].max
55
+ max_y = [bb_point_1.y, bb_point_2.y].max
56
+ min_x = [bb_point_1.x, bb_point_2.x].min
57
+ min_y = [bb_point_1.y, bb_point_2.y].min
58
+
59
+ !(point.x < min_x || point.x > max_x || point.y < min_y || point.y > max_y)
60
+ end
61
+
62
+ def bounding_box
63
+ max_x, min_x, max_y, min_y = -Float::MAX, Float::MAX, -Float::MAX, Float::MAX
64
+ each do |point|
65
+ max_y = point.y if point.y > max_y
66
+ min_y = point.y if point.y < min_y
67
+ max_x = point.x if point.x > max_x
68
+ min_x = point.x if point.x < min_x
69
+ end
70
+ [Point.new(min_x, max_y), Point.new(max_x, min_y)]
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,12 @@
1
+ module BorderPatrol
2
+ class Region < Set
3
+ def contains_point?(*point)
4
+ point = case point.length
5
+ when 1 then point.first
6
+ when 2 then BorderPatrol::Point.new(point[0],point[1])
7
+ else raise ArgumentError, "#{point} is invalid. Arguments can either be an object, or a longitude,lattitude pair."
8
+ end
9
+ any? { |polygon| polygon.contains_point?(point) }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,139 @@
1
+ require 'spec_helper'
2
+
3
+ describe BorderPatrol::Polygon do
4
+ describe "==" do
5
+ it "is true if polygons are congruent" do
6
+ points = [BorderPatrol::Point.new(1, 2), BorderPatrol::Point.new(3, 4), BorderPatrol::Point.new(0, 0)]
7
+ poly1 = BorderPatrol::Polygon.new(points)
8
+ poly2 = BorderPatrol::Polygon.new(points.unshift(points.pop))
9
+
10
+ poly1.should == poly2
11
+ poly2.should == poly1
12
+ poly3 = BorderPatrol::Polygon.new(points.reverse)
13
+ poly1.should == poly3
14
+ poly3.should == poly1
15
+
16
+ end
17
+
18
+ it "cares about order of points" do
19
+ points = [BorderPatrol::Point.new(1, 2), BorderPatrol::Point.new(3, 4), BorderPatrol::Point.new(5,5), BorderPatrol::Point.new(0, 0)]
20
+ poly1 = BorderPatrol::Polygon.new(points)
21
+ points = [BorderPatrol::Point.new(5,5), BorderPatrol::Point.new(1, 2), BorderPatrol::Point.new(0, 0), BorderPatrol::Point.new(3, 4)]
22
+ poly2 = BorderPatrol::Polygon.new(points)
23
+
24
+ poly1.should_not == poly2
25
+ poly2.should_not == poly1
26
+
27
+ end
28
+
29
+ it "is false if one polygon is a subset" do
30
+ poly1 = BorderPatrol::Polygon.new(BorderPatrol::Point.new(1, 2), BorderPatrol::Point.new(3, 4), BorderPatrol::Point.new(0, 0))
31
+ poly2 = BorderPatrol::Polygon.new(BorderPatrol::Point.new(1, 2), BorderPatrol::Point.new(3, 4), BorderPatrol::Point.new(0, 0), BorderPatrol::Point.new(4, 4))
32
+ poly2.should_not == poly1
33
+ poly1.should_not == poly2
34
+ end
35
+
36
+ it "is false if the polygons are not congruent" do
37
+ poly1 = BorderPatrol::Polygon.new(BorderPatrol::Point.new(1, 2), BorderPatrol::Point.new(3, 4), BorderPatrol::Point.new(0, 0))
38
+ poly2 = BorderPatrol::Polygon.new(BorderPatrol::Point.new(2, 1), BorderPatrol::Point.new(3, 4), BorderPatrol::Point.new(0, 0))
39
+ poly2.should_not == poly1
40
+ poly1.should_not == poly2
41
+ end
42
+ end
43
+
44
+ describe "#initialize" do
45
+ it "stores a list of points" do
46
+ points = [BorderPatrol::Point.new(1, 2), BorderPatrol::Point.new(3, 4), BorderPatrol::Point.new(0, 0)]
47
+ polygon = BorderPatrol::Polygon.new(points)
48
+ points.each do |point|
49
+ polygon.should include point
50
+ end
51
+ end
52
+
53
+ it "can be instantiated with a arbitrary argument list" do
54
+ points = [BorderPatrol::Point.new(1, 2), BorderPatrol::Point.new(3, 4), BorderPatrol::Point.new(0, 0)]
55
+ poly1 = BorderPatrol::Polygon.new(*points)
56
+ poly2 = BorderPatrol::Polygon.new(points)
57
+ poly1.should == poly2
58
+ end
59
+
60
+ it "raises if less than 3 points are given" do
61
+ points = [BorderPatrol::Point.new(1, 2), BorderPatrol::Point.new(2, 3)]
62
+ expect { BorderPatrol::Polygon.new(points) }.to raise_exception(BorderPatrol::InsufficientPointsToActuallyFormAPolygonError)
63
+ points = [BorderPatrol::Point.new(1, 2)]
64
+ expect { BorderPatrol::Polygon.new(points) }.to raise_exception(BorderPatrol::InsufficientPointsToActuallyFormAPolygonError)
65
+ points = []
66
+ expect { BorderPatrol::Polygon.new(points) }.to raise_exception(BorderPatrol::InsufficientPointsToActuallyFormAPolygonError)
67
+ end
68
+
69
+ it "doesn't store duplicated points" do
70
+ points = [BorderPatrol::Point.new(1, 2), BorderPatrol::Point.new(3, 4), BorderPatrol::Point.new(0, 0)]
71
+ duplicate_point = [BorderPatrol::Point.new(1, 2)]
72
+ polygon = BorderPatrol::Polygon.new(points + duplicate_point)
73
+ polygon.size.should == 3
74
+ points.each do |point|
75
+ polygon.should include point
76
+ end
77
+ end
78
+ end
79
+
80
+ describe "#bounding_box" do
81
+ it "returns the (max top, max left), (max bottom, max right) as points" do
82
+ points = [BorderPatrol::Point.new(-1, 3), BorderPatrol::Point.new(4, -3), BorderPatrol::Point.new(10, 4), BorderPatrol::Point.new(0, 12)]
83
+ polygon = BorderPatrol::Polygon.new(points)
84
+ polygon.bounding_box.should == [BorderPatrol::Point.new(-1, 12), BorderPatrol::Point.new(10, -3)]
85
+ end
86
+ end
87
+
88
+ describe "#contains_point?" do
89
+ before do
90
+ points = [BorderPatrol::Point.new(-10, 0), BorderPatrol::Point.new(10, 0), BorderPatrol::Point.new(0, 10)]
91
+ @polygon = BorderPatrol::Polygon.new(points)
92
+ end
93
+
94
+ it "is true if the point is in the polygon" do
95
+ @polygon.contains_point?(BorderPatrol::Point.new(0.5, 0.5)).should be_true
96
+ @polygon.contains_point?(BorderPatrol::Point.new(0, 5)).should be_true
97
+ @polygon.contains_point?(BorderPatrol::Point.new(-1, 3)).should be_true
98
+ end
99
+
100
+ it "does not include points on the lines with slopes between vertices" do
101
+ @polygon.contains_point?(BorderPatrol::Point.new(5.0, 5.0)).should be_false
102
+ @polygon.contains_point?(BorderPatrol::Point.new(4.999999, 4.9999999)).should be_true
103
+ @polygon.contains_point?(BorderPatrol::Point.new(0, 0)).should be_true
104
+ @polygon.contains_point?(BorderPatrol::Point.new(0.000001, 0.000001)).should be_true
105
+ end
106
+
107
+ it "includes points at the vertices" do
108
+ @polygon.contains_point?(BorderPatrol::Point.new(-10, 0)).should be_true
109
+ end
110
+
111
+ it "is false if the point is outside of the polygon" do
112
+ @polygon.contains_point?(BorderPatrol::Point.new(9, 5)).should be_false
113
+ @polygon.contains_point?(BorderPatrol::Point.new(-5, 8)).should be_false
114
+ @polygon.contains_point?(BorderPatrol::Point.new(-10, -1)).should be_false
115
+ @polygon.contains_point?(BorderPatrol::Point.new(-20, -20)).should be_false
116
+ end
117
+ end
118
+
119
+ describe "#inside_bounding_box?" do
120
+ before do
121
+ points = [BorderPatrol::Point.new(-10, 0), BorderPatrol::Point.new(10, 0), BorderPatrol::Point.new(0, 10)]
122
+ @polygon = BorderPatrol::Polygon.new(points)
123
+ end
124
+
125
+ it "is false if it is outside the bounding box" do
126
+ @polygon.inside_bounding_box?(BorderPatrol::Point.new(-10, -1)).should be_false
127
+ @polygon.inside_bounding_box?(BorderPatrol::Point.new(-20, -20)).should be_false
128
+ @polygon.inside_bounding_box?(BorderPatrol::Point.new(1, 20)).should be_false
129
+ end
130
+
131
+ it "returns true if it is inside the bounding box" do
132
+ @polygon.inside_bounding_box?(BorderPatrol::Point.new(9, 5)).should be_true
133
+ @polygon.inside_bounding_box?(BorderPatrol::Point.new(-5, 8)).should be_true
134
+ @polygon.inside_bounding_box?(BorderPatrol::Point.new(1, 1)).should be_true
135
+ end
136
+
137
+ end
138
+ end
139
+
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe BorderPatrol::Region do
4
+ it "is a Set" do
5
+ BorderPatrol::Region.new.should be_a Set
6
+ end
7
+
8
+ describe "#contains_point?" do
9
+ def point
10
+
11
+ end
12
+ def polygon(start=0)
13
+ BorderPatrol::Polygon.new(
14
+ BorderPatrol::Point.new(start,start),
15
+ BorderPatrol::Point.new(start + 10, start),
16
+ BorderPatrol::Point.new(start + 10, start + 10),
17
+ BorderPatrol::Point.new(start, start + 10))
18
+ end
19
+ subject { BorderPatrol::Region.new(@polygons) }
20
+
21
+ it "raises an argument error if contains_point? takes more than 3 arguments" do
22
+ expect { subject.contains_point? }.to raise_exception ArgumentError
23
+ expect { subject.contains_point?(1,2,3) }.to raise_exception ArgumentError
24
+ end
25
+
26
+ it "returns true if any polygon contains the point" do
27
+ point = BorderPatrol::Point.new(1,2)
28
+ @polygons = [polygon, polygon(30)]
29
+
30
+ subject.contains_point?(point).should be_true
31
+ end
32
+
33
+ it "returns false if no polygons contain the point" do
34
+ point = BorderPatrol::Point.new(-1,-2)
35
+ @polygons = [polygon, polygon(30)]
36
+
37
+ subject.contains_point?(point).should be_false
38
+ end
39
+
40
+ it "transforms (x,y) coordinates passed in into a point" do
41
+ @polygons = [polygon, polygon(30)]
42
+
43
+ subject.contains_point?(1,2).should be_true
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe BorderPatrol do
4
+ describe ".parse_kml" do
5
+ it "returns a BorderPatrol::Region containing a BorderPatrol::Polygon for each polygon in the KML file" do
6
+ kml_data = File.read("#{File.dirname(__FILE__)}/../support/multi-polygon-test.kml")
7
+ region = BorderPatrol.parse_kml(kml_data)
8
+ region.length.should == 3
9
+ region.each {|p| p.should be_a BorderPatrol::Polygon}
10
+ end
11
+
12
+ context "when there is only one polygon" do
13
+ it "returns a region containing a single polygon" do
14
+ kml_data = File.read("#{File.dirname(__FILE__)}/../support/colorado-test.kml")
15
+ region = BorderPatrol.parse_kml(kml_data)
16
+ region.length.should == 1
17
+ region.each {|p| p.should be_a BorderPatrol::Polygon}
18
+ end
19
+ end
20
+ end
21
+
22
+ describe ".parse_kml_polygon_data" do
23
+ it "returns a BorderPatrol::Polygon with the points from the kml data in the correct order" do
24
+ kml = <<-EOM
25
+ <Polygon>
26
+ <outerBoundaryIs>
27
+ <LinearRing>
28
+ <tessellate>1</tessellate>
29
+ <coordinates>
30
+ -10,25,0.000000
31
+ -1,30,0.000000
32
+ 10,1,0.000000
33
+ 0, -5,000000
34
+ -10,25,0.000000
35
+ </coordinates>
36
+ </LinearRing>
37
+ </outerBoundaryIs>
38
+ </Polygon>
39
+ EOM
40
+ polygon = BorderPatrol.parse_kml_polygon_data(kml)
41
+ polygon.should == BorderPatrol::Polygon.new(BorderPatrol::Point.new(-10, 25), BorderPatrol::Point.new(-1, 30), BorderPatrol::Point.new(10, 1), BorderPatrol::Point.new(0, -5))
42
+ end
43
+ end
44
+
45
+ describe BorderPatrol::Point do
46
+ describe "==" do
47
+ it "is true if both points contain the same values" do
48
+ BorderPatrol::Point.new(1,2).should == BorderPatrol::Point.new(1,2)
49
+ end
50
+
51
+ it "is true if one point contains floats and one contains integers" do
52
+ BorderPatrol::Point.new(1,2.0).should == BorderPatrol::Point.new(1.0,2)
53
+ end
54
+
55
+ it "is false if the points contain different values" do
56
+ BorderPatrol::Point.new(1,3).should_not == BorderPatrol::Point.new(1.0,2)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format Spec::Runner::Formatter::CompactProgressBarFormatter
3
+ --loadby mtime
4
+ --backtrace
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+
5
+ require 'nokogiri'
6
+
7
+ base_dir = File.expand_path("#{File.dirname(__FILE__)}/..")
8
+ app_dirs = ['lib', 'lib/kaml_pen']
9
+
10
+ Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
11
+
12
+ app_dirs.each do |app_dir|
13
+ Dir["#{base_dir}/spec/#{app_dir}/*.rb"].each do |f|
14
+ f.sub!('/spec','')
15
+ f.sub!('_spec','')
16
+ require f
17
+ end
18
+ end
19
+
20
+ Spec::Runner.configure do |config|
21
+ end
@@ -0,0 +1,37 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <kml xmlns="http://earth.google.com/kml/2.2">
3
+ <Document>
4
+ <name>Colorado</name>
5
+ <description><![CDATA[]]></description>
6
+ <Style id="style1">
7
+ <LineStyle>
8
+ <color>40000000</color>
9
+ <width>3</width>
10
+ </LineStyle>
11
+ <PolyStyle>
12
+ <color>73FF0000</color>
13
+ <fill>1</fill>
14
+ <outline>1</outline>
15
+ </PolyStyle>
16
+ </Style>
17
+ <Placemark>
18
+ <name>Shape 1</name>
19
+ <description><![CDATA[]]></description>
20
+ <styleUrl>#style1</styleUrl>
21
+ <Polygon>
22
+ <outerBoundaryIs>
23
+ <LinearRing>
24
+ <tessellate>1</tessellate>
25
+ <coordinates>
26
+ -109.050293,41.008923,0.000000
27
+ -102.057495,41.004776,0.000000
28
+ -102.046509,36.993778,0.000000
29
+ -109.044800,36.998165,0.000000
30
+ -109.050293,41.008923,0.000000
31
+ </coordinates>
32
+ </LinearRing>
33
+ </outerBoundaryIs>
34
+ </Polygon>
35
+ </Placemark>
36
+ </Document>
37
+ </kml>
@@ -0,0 +1,133 @@
1
+ # Originally by Nicholas A. Evans. See LICENSE.txt at
2
+ # http://gist.github.com/275257
3
+ #
4
+ # Note: includes modifications from the original to use '=' for the progress
5
+ # bar mark and keep the cursor from hopping all around during the animation.
6
+ #
7
+ # This version is compatible with RSpec 1.2.9 and ProgressBar 0.9.0.
8
+
9
+ require 'spec/runner/formatter/base_text_formatter'
10
+ require 'progressbar'
11
+
12
+ module Spec
13
+ module Runner
14
+ module Formatter
15
+ class CompactProgressBarFormatter < BaseTextFormatter
16
+ # Threshold for slow specs, in seconds.
17
+ # Anything that takes longer than this will be printed out
18
+ THRESHOLD = 1.0 unless defined?(THRESHOLD)
19
+
20
+ attr_reader :total, :current
21
+
22
+ def start(example_count)
23
+ @current = 0
24
+ @total = example_count
25
+ @error_state = :all_passing
26
+ @pbar = ProgressBar.new("#{example_count} examples", example_count, output)
27
+ @pbar.instance_variable_set("@bar_mark", "=")
28
+ end
29
+
30
+ def example_started(example)
31
+ super
32
+ @start_time = Time.now
33
+ end
34
+
35
+ def example_passed(example)
36
+ print_warning_if_slow(example_group.description,
37
+ example.description,
38
+ Time.now - @start_time)
39
+ increment
40
+ end
41
+
42
+ def example_pending(example, message, deprecated_pending_location=nil)
43
+ immediately_dump_pending(example.description, message, pending_caller)
44
+ mark_error_state_pending
45
+ increment
46
+ end
47
+
48
+ def example_failed(example, counter, failure)
49
+ immediately_dump_failure(counter, failure)
50
+ mark_error_state_failed
51
+ increment
52
+ end
53
+
54
+ def start_dump
55
+ with_color do
56
+ @pbar.finish
57
+ end
58
+ output.flush
59
+ end
60
+
61
+ def dump_failure(*args)
62
+ # no-op; we summarized failures as we were running
63
+ end
64
+
65
+ def method_missing(sym, *args)
66
+ # ignore
67
+ end
68
+
69
+ # Adapted from BaseTextFormatter#dump_failure
70
+ def immediately_dump_failure(counter, failure)
71
+ erase_current_line
72
+ output.print "#{counter.to_s}) "
73
+ output.puts colorize_failure("#{failure.header}\n#{failure.exception.message}", failure)
74
+ output.puts format_backtrace(failure.exception.backtrace)
75
+ output.puts
76
+ end
77
+
78
+ # Adapted from BaseTextFormatter#dump_pending
79
+ def immediately_dump_pending(desc, msg, called_from)
80
+ erase_current_line
81
+ output.puts yellow("PENDING SPEC:") + " #{desc} (#{msg})"
82
+ output.puts " Called from #{called_from}" if called_from
83
+ end
84
+
85
+ def increment
86
+ with_color do
87
+ @current += 1
88
+ # Since we're constantly erasing the line, make sure the progress is
89
+ # printed even when the bar hasn't changed
90
+ @pbar.instance_variable_set("@previous", 0)
91
+ @pbar.instance_variable_set("@title", " #{current}/#{total}")
92
+ @pbar.inc
93
+ end
94
+ output.flush
95
+ end
96
+
97
+ ERROR_STATE_COLORS = {
98
+ :all_passing => "\e[32m", # green
99
+ :some_pending => "\e[33m", # yellow
100
+ :some_failed => "\e[31m", # red
101
+ } unless defined?(ERROR_STATE_COLORS)
102
+
103
+ def with_color
104
+ output.print "\e[?25l" + ERROR_STATE_COLORS[@error_state] if colour?
105
+ yield
106
+ output.print "\e[0m\e[?25h" if colour?
107
+ end
108
+
109
+ def mark_error_state_failed
110
+ @error_state = :some_failed
111
+ end
112
+
113
+ def mark_error_state_pending
114
+ @error_state = :some_pending unless @error_state == :some_failed
115
+ end
116
+
117
+ def erase_current_line
118
+ output.print "\e[K"
119
+ end
120
+
121
+ def print_warning_if_slow(group, example, elapsed)
122
+ if elapsed > THRESHOLD
123
+ erase_current_line
124
+ output.print yellow("SLOW SPEC: #{sprintf("%.4f", elapsed)} ")
125
+ output.print " #{group} #{example}"
126
+ output.puts
127
+ end
128
+ end
129
+
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,100 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <kml xmlns="http://earth.google.com/kml/2.2">
3
+ <Document>
4
+ <name>Multi-Polygon</name>
5
+ <description><![CDATA[]]></description>
6
+ <Style id="style1">
7
+ <LineStyle>
8
+ <color>40000000</color>
9
+ <width>3</width>
10
+ </LineStyle>
11
+ <PolyStyle>
12
+ <color>73FF0000</color>
13
+ <fill>1</fill>
14
+ <outline>1</outline>
15
+ </PolyStyle>
16
+ </Style>
17
+ <Style id="style3">
18
+ <LineStyle>
19
+ <color>40000000</color>
20
+ <width>3</width>
21
+ </LineStyle>
22
+ <PolyStyle>
23
+ <color>73FF0000</color>
24
+ <fill>1</fill>
25
+ <outline>1</outline>
26
+ </PolyStyle>
27
+ </Style>
28
+ <Style id="style2">
29
+ <LineStyle>
30
+ <color>40000000</color>
31
+ <width>3</width>
32
+ </LineStyle>
33
+ <PolyStyle>
34
+ <color>73FF0000</color>
35
+ <fill>1</fill>
36
+ <outline>1</outline>
37
+ </PolyStyle>
38
+ </Style>
39
+ <Placemark>
40
+ <name>Colorado</name>
41
+ <description><![CDATA[]]></description>
42
+ <styleUrl>#style1</styleUrl>
43
+ <Polygon>
44
+ <outerBoundaryIs>
45
+ <LinearRing>
46
+ <tessellate>1</tessellate>
47
+ <coordinates>
48
+ -109.053040,41.002705,0.000000
49
+ -102.046509,41.006847,0.000000
50
+ -102.041016,36.991585,0.000000
51
+ -109.048920,36.997070,0.000000
52
+ -109.053040,41.002705,0.000000
53
+ </coordinates>
54
+ </LinearRing>
55
+ </outerBoundaryIs>
56
+ </Polygon>
57
+ </Placemark>
58
+ <Placemark>
59
+ <name>Paris</name>
60
+ <description><![CDATA[]]></description>
61
+ <styleUrl>#style3</styleUrl>
62
+ <Polygon>
63
+ <outerBoundaryIs>
64
+ <LinearRing>
65
+ <tessellate>1</tessellate>
66
+ <coordinates>
67
+ 1.593018,49.109837,0.000000
68
+ 1.571045,48.257599,0.000000
69
+ 3.186035,48.275883,0.000000
70
+ 3.186035,49.167339,0.000000
71
+ 1.593018,49.109837,0.000000
72
+ </coordinates>
73
+ </LinearRing>
74
+ </outerBoundaryIs>
75
+ </Polygon>
76
+ </Placemark>
77
+ <Placemark>
78
+ <name>Australia</name>
79
+ <description><![CDATA[]]></description>
80
+ <styleUrl>#style2</styleUrl>
81
+ <Polygon>
82
+ <outerBoundaryIs>
83
+ <LinearRing>
84
+ <tessellate>1</tessellate>
85
+ <coordinates>
86
+ 120.673828,-14.179186,0.000000
87
+ 107.534180,-25.918526,0.000000
88
+ 111.840820,-37.544579,0.000000
89
+ 148.183594,-44.559162,0.000000
90
+ 157.543945,-30.789038,0.000000
91
+ 148.051758,-10.617418,0.000000
92
+ 135.131836,-9.188870,0.000000
93
+ 120.673828,-14.179186,0.000000
94
+ </coordinates>
95
+ </LinearRing>
96
+ </outerBoundaryIs>
97
+ </Polygon>
98
+ </Placemark>
99
+ </Document>
100
+ </kml>
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: border_patrol
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Zach Brock
14
+ - Matt Wilson
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-10-20 00:00:00 -07:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ version_requirements: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - "="
27
+ - !ruby/object:Gem::Version
28
+ hash: 49
29
+ segments:
30
+ - 0
31
+ - 8
32
+ - 7
33
+ version: 0.8.7
34
+ name: rake
35
+ prerelease: false
36
+ requirement: *id001
37
+ type: :development
38
+ - !ruby/object:Gem::Dependency
39
+ version_requirements: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - "="
43
+ - !ruby/object:Gem::Version
44
+ hash: 27
45
+ segments:
46
+ - 1
47
+ - 3
48
+ - 0
49
+ version: 1.3.0
50
+ name: rspec
51
+ prerelease: false
52
+ requirement: *id002
53
+ type: :development
54
+ - !ruby/object:Gem::Dependency
55
+ version_requirements: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ name: progressbar
65
+ prerelease: false
66
+ requirement: *id003
67
+ type: :development
68
+ - !ruby/object:Gem::Dependency
69
+ version_requirements: &id004 !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - "="
73
+ - !ruby/object:Gem::Version
74
+ hash: 113
75
+ segments:
76
+ - 1
77
+ - 4
78
+ - 3
79
+ - 1
80
+ version: 1.4.3.1
81
+ name: nokogiri
82
+ prerelease: false
83
+ requirement: *id004
84
+ type: :runtime
85
+ description: Lets you import a KML file and then check if points are inside or outside the polygons the file defines.
86
+ email: eng@squareup.com
87
+ executables: []
88
+
89
+ extensions: []
90
+
91
+ extra_rdoc_files: []
92
+
93
+ files:
94
+ - lib/border_patrol/polygon.rb
95
+ - lib/border_patrol/region.rb
96
+ - lib/border_patrol.rb
97
+ - lib/VERSION
98
+ - spec/lib/border_patrol/polygon_spec.rb
99
+ - spec/lib/border_patrol/region_spec.rb
100
+ - spec/lib/border_patrol_spec.rb
101
+ - spec/spec.opts
102
+ - spec/spec_helper.rb
103
+ - spec/support/colorado-test.kml
104
+ - spec/support/formatters/compact_progress_bar_formatter.rb
105
+ - spec/support/multi-polygon-test.kml
106
+ - border_patrol.gemspec
107
+ - Rakefile
108
+ - README.markdown
109
+ - .gitignore
110
+ - Gemfile
111
+ - Gemfile.lock
112
+ has_rdoc: true
113
+ homepage: http://github.com/square/border_patrol
114
+ licenses: []
115
+
116
+ post_install_message:
117
+ rdoc_options:
118
+ - --charset=UTF-8
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ hash: 3
127
+ segments:
128
+ - 0
129
+ version: "0"
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ hash: 3
136
+ segments:
137
+ - 0
138
+ version: "0"
139
+ requirements: []
140
+
141
+ rubyforge_project:
142
+ rubygems_version: 1.3.7
143
+ signing_key:
144
+ specification_version: 3
145
+ summary: Import and query KML regions
146
+ test_files:
147
+ - spec/lib/border_patrol/polygon_spec.rb
148
+ - spec/lib/border_patrol/region_spec.rb
149
+ - spec/lib/border_patrol_spec.rb
150
+ - spec/spec.opts
151
+ - spec/spec_helper.rb
152
+ - spec/support/colorado-test.kml
153
+ - spec/support/formatters/compact_progress_bar_formatter.rb
154
+ - spec/support/multi-polygon-test.kml