border_patrol 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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