local-geocoder 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MGIwNmFlYmZlNGVmNDExZDZkOTMxZGJmODU4NGNhNDIyMzY3MTZkNA==
5
+ data.tar.gz: !binary |-
6
+ ZDIxMWZmNmVmZGYwZTZmYmFkYWVhNDg3YjhhYTY4ZTNhOGQ5YmJjMQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ ODc3YjAzMGNiNjQyZTU3NDgwOTAzOTI2ZWZjOGYzOTk1ODNjNWMyZDQ3MWIz
10
+ ZThkMjQwMDRmMTRkN2ZlOTdhYTQ0Njg0YzY3NWQzMTUxM2I4YWQ1MmQ3ZDc1
11
+ NTQ3ODE1N2MyMTFjYjIyYzMzOTEwZjkyZGM1N2JjYzM2NGFmZjY=
12
+ data.tar.gz: !binary |-
13
+ ZGRkNGQ3NjQ2OTE4MTUyYzU5Yjg3MzVhODg5Yjg4ZWI2MjNmY2UxZmE0YzQw
14
+ ZDAyZmNiZWNmYTJiNmY1ZjU5ODQ5MjRjMTlmODQ2OTgwMjYyMWY3YmE3ZTVj
15
+ YTg0NGExZjA1MDFiYzQ0ZGUyMjQ5MjIwN2FlODNlNWViZjA0OTU=
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "../lib"))
4
+ require "local_geocoder"
5
+ require "trollop"
6
+ require "csv"
7
+
8
+ DEFAULT_TEMPLATE = '(?<lng>\-?\d+.?\d+)[,\t ]\s?(?<lat>\-?\d+.?\d+)'
9
+
10
+ opts = Trollop::options do
11
+ version "local_geocode #{LocalGeocoder::VERSION}"
12
+ banner <<-EOS
13
+ Local Geocoder
14
+ ---------------
15
+ Reverse geocodes lng,lat pairs into Country codes (and State + County within the USA). Each line of Stdin is read and has lng,lat pairs extracted (see --template below for how) and the resulting reverse geocode is output to Stdout.
16
+
17
+ Usage:
18
+ local_geocode [options] file
19
+
20
+ Example:
21
+ > echo "-122.4194155, 37.7749295" | local_geocode
22
+ > United States of America (USA), California (CA), San Francisco
23
+
24
+ Options:
25
+ EOS
26
+ opt 'template', "The regex used to parse lng,lat paris from the input stream", :default => DEFAULT_TEMPLATE, :short => :t
27
+ end
28
+
29
+ require "local_geocoder"
30
+
31
+ lc = LocalGeocoder::Geocoder.new
32
+ ARGF.each_line do |l|
33
+ data = l.match(Regexp.new(opts["template"]))
34
+ result = if data.nil?
35
+ LocalGeocoder::Result.new
36
+ else
37
+ lng, lat = data[:lng].to_f, data[:lat].to_f
38
+ lc.reverse_geocode(lng, lat)
39
+ end
40
+ puts result.inspect
41
+
42
+ end
@@ -0,0 +1,49 @@
1
+ package geometry;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyArray;
5
+ import org.jruby.RubyClass;
6
+ import org.jruby.RubyFixnum;
7
+ import org.jruby.RubyModule;
8
+ import org.jruby.RubyObject;
9
+ import org.jruby.anno.JRubyClass;
10
+ import org.jruby.anno.JRubyMethod;
11
+ import org.jruby.runtime.ObjectAllocator;
12
+ import org.jruby.runtime.ThreadContext;
13
+ import org.jruby.runtime.builtin.IRubyObject;
14
+ import org.jruby.runtime.load.BasicLibraryService;
15
+
16
+ public class GeometryService implements BasicLibraryService {
17
+ private Ruby runtime;
18
+
19
+ public boolean basicLoad(Ruby runtime) {
20
+ this.runtime = runtime;
21
+ RubyModule lc = runtime.defineModule("LocalGeocoder");
22
+ RubyModule geo = lc.defineModuleUnder("Geometry");
23
+
24
+ RubyClass point = geo.defineClassUnder("Point", runtime.getObject(), new ObjectAllocator() {
25
+ public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) {
26
+ return new RPoint(runtime, rubyClass);
27
+ }
28
+ });
29
+
30
+ RubyClass rect = geo.defineClassUnder("Rect", runtime.getObject(), new ObjectAllocator() {
31
+ public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) {
32
+ return new RRect(runtime, rubyClass);
33
+ }
34
+ });
35
+
36
+ RubyClass polygon = geo.defineClassUnder("Polygon", runtime.getObject(), new ObjectAllocator() {
37
+ public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) {
38
+ return new RPolygon(runtime, rubyClass);
39
+ }
40
+ });
41
+
42
+ point.defineAnnotatedMethods(RPoint.class);
43
+ rect.defineAnnotatedMethods(RRect.class);
44
+ polygon.defineAnnotatedMethods(RPolygon.class);
45
+ return true;
46
+ }
47
+
48
+ }
49
+
@@ -0,0 +1,27 @@
1
+ package geometry;
2
+
3
+ public class Point {
4
+
5
+ double x;
6
+ double y;
7
+
8
+ public Point(double x, double y) {
9
+ this.x = x;
10
+ this.y = y;
11
+ }
12
+
13
+ public double getX() {
14
+ return this.x;
15
+ }
16
+
17
+ public double getY() {
18
+ return this.y;
19
+ }
20
+
21
+ public String toString() {
22
+ return String.format("[%s,%s]", x, y);
23
+ }
24
+
25
+ }
26
+
27
+
@@ -0,0 +1,88 @@
1
+ package geometry;
2
+
3
+ public class Polygon {
4
+
5
+ Point[] points;
6
+ Rect boundingBox;
7
+
8
+ public Polygon(Point[] points) {
9
+ this.points = points;
10
+ }
11
+
12
+ public Point[] getPoints() {
13
+ return this.points;
14
+ }
15
+
16
+ public int numberOfPoints() {
17
+ return this.points.length;
18
+ }
19
+
20
+ public Rect boundingBox() {
21
+ if (boundingBox == null) {
22
+ double minX, minY, maxX, maxY;
23
+ minX = minY = Double.POSITIVE_INFINITY;
24
+ maxX = maxY = Double.NEGATIVE_INFINITY;
25
+
26
+ for (Point p: this.points) {
27
+
28
+ double x = p.getX();
29
+ double y = p.getY();
30
+
31
+ if (x < minX)
32
+ minX = x;
33
+ if (x > maxX)
34
+ maxX = x;
35
+
36
+ if (y < minY)
37
+ minY = y;
38
+ if (y > maxY)
39
+ maxY = y;
40
+ }
41
+
42
+ this.boundingBox = new Rect(minX, minY, maxX-minX, maxY-minY);
43
+ }
44
+
45
+ return this.boundingBox;
46
+ }
47
+
48
+ public String toString() {
49
+ StringBuilder sb = new StringBuilder();
50
+
51
+ for (Point p : points) {
52
+ sb.append( p.toString() + "," );
53
+ }
54
+ return sb.toString();
55
+ }
56
+
57
+ public boolean containsPoint(Point point) {
58
+ if (!this.boundingBox().containsPoint(point))
59
+ return false;
60
+
61
+ boolean containsPoint = false;
62
+ int i = -1;
63
+ int j = this.points.length - 1;
64
+ while (++i < this.points.length) {
65
+ Point p1 = this.points[i];
66
+ Point p2 = this.points[j];
67
+ if (this.withinYBounds(point, p1, p2)) {
68
+ if (this.intersectsLineSegment(point, p1, p2)) {
69
+ containsPoint = !containsPoint;
70
+ }
71
+ }
72
+ j = i;
73
+ }
74
+ return containsPoint;
75
+ }
76
+
77
+ private boolean intersectsLineSegment(Point point, Point p1, Point p2) {
78
+ return (point.getX() < (p2.getX() - p1.getX()) * (point.getY() - p1.getY()) / (p2.getY() - p1.getY()) + p1.getX());
79
+ }
80
+
81
+ private boolean withinYBounds(Point point, Point p1, Point p2) {
82
+ return (p1.getY() <= point.getY() && point.getY() < p2.getY()) ||
83
+ (p2.getY() <= point.getY() && point.getY() < p1.getY());
84
+ }
85
+
86
+ }
87
+
88
+
@@ -0,0 +1,72 @@
1
+ package geometry;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyArray;
5
+ import org.jruby.RubyClass;
6
+ import org.jruby.RubyFixnum;
7
+ import org.jruby.RubyModule;
8
+ import org.jruby.RubyObject;
9
+ import org.jruby.anno.JRubyClass;
10
+ import org.jruby.anno.JRubyMethod;
11
+ import org.jruby.runtime.ObjectAllocator;
12
+ import org.jruby.runtime.ThreadContext;
13
+ import org.jruby.runtime.builtin.IRubyObject;
14
+ import org.jruby.runtime.load.BasicLibraryService;
15
+
16
+ @JRubyClass(name = "LocalGeocoder::Geometry::Point")
17
+ public class RPoint extends RubyObject {
18
+
19
+ Point point;
20
+ IRubyObject data;
21
+
22
+ public RPoint(final Ruby runtime, RubyClass rubyClass) {
23
+ super(runtime, rubyClass);
24
+ }
25
+
26
+ @JRubyMethod(name = "initialize", required = 2, optional = 1)
27
+ public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
28
+ Ruby ruby = context.getRuntime();
29
+ double x = args[0].convertToFloat().getDoubleValue();
30
+ double y = args[1].convertToFloat().getDoubleValue();
31
+
32
+ this.point = new Point(x,y);
33
+ this.data = context.nil;
34
+ if (args.length > 2) {
35
+ this.data = args[2];
36
+ }
37
+
38
+ return context.nil;
39
+ }
40
+
41
+ public void setPoint(Point point) {
42
+ this.point = point;
43
+ }
44
+
45
+ public Point getJava() {
46
+ return this.point;
47
+ }
48
+
49
+ @JRubyMethod(name = "x")
50
+ public IRubyObject x(ThreadContext context) {
51
+ Ruby ruby = context.getRuntime();
52
+ return ruby.newFloat(point.getX());
53
+ }
54
+
55
+ @JRubyMethod(name = "y")
56
+ public IRubyObject y(ThreadContext context) {
57
+ Ruby ruby = context.getRuntime();
58
+ return ruby.newFloat(point.getY());
59
+ }
60
+
61
+ @JRubyMethod(name = "data")
62
+ public IRubyObject data(ThreadContext context) {
63
+ return data;
64
+ }
65
+
66
+ @JRubyMethod(name = "inspect")
67
+ public IRubyObject inspect(ThreadContext context) {
68
+ Ruby ruby = context.getRuntime();
69
+ return ruby.newString(point.toString());
70
+ }
71
+
72
+ }
@@ -0,0 +1,111 @@
1
+ package geometry;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyArray;
5
+ import org.jruby.RubyClass;
6
+ import org.jruby.RubyFixnum;
7
+ import org.jruby.RubyModule;
8
+ import org.jruby.RubyObject;
9
+ import org.jruby.anno.JRubyClass;
10
+ import org.jruby.anno.JRubyMethod;
11
+ import org.jruby.runtime.ObjectAllocator;
12
+ import org.jruby.runtime.ThreadContext;
13
+ import org.jruby.runtime.builtin.IRubyObject;
14
+ import org.jruby.runtime.load.BasicLibraryService;
15
+
16
+ @JRubyClass(name = "LocalGeocoder::Geometry::Polygon")
17
+ public class RPolygon extends RubyObject {
18
+
19
+ Polygon polygon;
20
+
21
+ public RPolygon(final Ruby runtime, RubyClass rubyClass) {
22
+ super(runtime, rubyClass);
23
+ }
24
+
25
+ public void setPolygon(Polygon polygon) {
26
+ this.polygon = polygon;
27
+ }
28
+
29
+ @JRubyMethod(name = "initialize")
30
+ public IRubyObject initialize(ThreadContext context, IRubyObject rps) {
31
+ Ruby ruby = context.getRuntime();
32
+
33
+ RubyArray rPoints = (RubyArray) rps;
34
+
35
+ Point[] points = new Point[rPoints.getLength()];
36
+ for (int i = 0; i < rPoints.getLength(); i++) {
37
+ points[i] = ((RPoint) rPoints.get(i)).getJava();
38
+ }
39
+
40
+ this.polygon = new Polygon(points);
41
+
42
+ return context.nil;
43
+ }
44
+
45
+ @JRubyMethod(meta = true, name = "from_point_array")
46
+ public static IRubyObject fromPointArray(ThreadContext context, IRubyObject klazz, IRubyObject pts) {
47
+ Ruby ruby = context.getRuntime();
48
+
49
+ RubyArray rpts = (RubyArray) pts;
50
+
51
+ Point[] points = new Point[rpts.getLength()];
52
+ for (int i = 0; i < rpts.getLength(); i++) {
53
+ double x = ((Number) ((RubyArray) rpts.get(i)).get(0)).doubleValue();
54
+ double y = ((Number) ((RubyArray) rpts.get(i)).get(1)).doubleValue();
55
+ points[i] = new Point(x,y);
56
+ }
57
+
58
+ IRubyObject obj = ((RubyClass) klazz).allocate();
59
+ ((RPolygon) obj).setPolygon(new Polygon(points));
60
+
61
+ return obj;
62
+ }
63
+
64
+ @JRubyMethod
65
+ public IRubyObject number_of_points(ThreadContext context) {
66
+ Ruby ruby = context.getRuntime();
67
+ int i = this.polygon.numberOfPoints();
68
+ return ruby.newFixnum(i);
69
+ }
70
+
71
+ @JRubyMethod(name = "points")
72
+ public IRubyObject points(ThreadContext context) {
73
+ Ruby ruby = context.getRuntime();
74
+
75
+ Point[] points = this.polygon.getPoints();
76
+ RPoint[] rPoints = new RPoint[points.length];
77
+ for (int i = 0; i < points.length; i++) {
78
+ RPoint rPoint = new RPoint(ruby, (RubyClass)ruby.getClassFromPath("LocalGeocoder::Geometry::Point"));
79
+ rPoint.setPoint(points[i]);
80
+ rPoints[i] = rPoint;
81
+ }
82
+
83
+ return ruby.newArray(rPoints);
84
+ }
85
+
86
+ @JRubyMethod(name = "bounding_box")
87
+ public IRubyObject boundingBox(ThreadContext context) {
88
+ Ruby ruby = context.getRuntime();
89
+
90
+ Rect rect = this.polygon.boundingBox();
91
+ RRect rRect = new RRect(ruby, (RubyClass)ruby.getClassFromPath("LocalGeocoder::Geometry::Rect"));
92
+ rRect.setRect(rect);
93
+
94
+ return rRect;
95
+ }
96
+
97
+ @JRubyMethod(name = "contains_point?")
98
+ public IRubyObject containsPoint(ThreadContext context, IRubyObject rPoint) {
99
+ Ruby ruby = context.getRuntime();
100
+
101
+ boolean result = this.polygon.containsPoint(((RPoint)rPoint).getJava());
102
+ return ruby.newBoolean(result);
103
+ }
104
+
105
+ @JRubyMethod(name = "inspect")
106
+ public IRubyObject inspect(ThreadContext context) {
107
+ Ruby ruby = context.getRuntime();
108
+ return ruby.newString(polygon.toString());
109
+ }
110
+
111
+ }
@@ -0,0 +1,96 @@
1
+ package geometry;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyArray;
5
+ import org.jruby.RubyClass;
6
+ import org.jruby.RubyFixnum;
7
+ import org.jruby.RubyModule;
8
+ import org.jruby.RubyObject;
9
+ import org.jruby.anno.JRubyClass;
10
+ import org.jruby.anno.JRubyMethod;
11
+ import org.jruby.runtime.ObjectAllocator;
12
+ import org.jruby.runtime.ThreadContext;
13
+ import org.jruby.runtime.builtin.IRubyObject;
14
+ import org.jruby.runtime.load.BasicLibraryService;
15
+
16
+ @JRubyClass(name = "LocalGeocoder::Geometry::Rect")
17
+ public class RRect extends RubyObject {
18
+
19
+ Rect rect;
20
+
21
+ public RRect(final Ruby runtime, RubyClass rubyClass) {
22
+ super(runtime, rubyClass);
23
+ }
24
+
25
+ @JRubyMethod(name = "initialize", required = 4)
26
+ public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
27
+ Ruby ruby = context.getRuntime();
28
+
29
+ double dX = args[0].convertToFloat().getDoubleValue();
30
+ double dY = args[1].convertToFloat().getDoubleValue();
31
+ double dWidth = args[2].convertToFloat().getDoubleValue();
32
+ double dHeight = args[3].convertToFloat().getDoubleValue();
33
+
34
+ this.rect = new Rect(dX,dY,dWidth,dHeight);
35
+
36
+ return context.nil;
37
+ }
38
+
39
+ public void setRect(Rect rect) {
40
+ this.rect = rect;
41
+ }
42
+
43
+ public Rect getJava() {
44
+ return this.rect;
45
+ }
46
+
47
+ @JRubyMethod(name = "x")
48
+ public IRubyObject x(ThreadContext context) {
49
+ Ruby ruby = context.getRuntime();
50
+ return ruby.newFloat(rect.getX());
51
+ }
52
+
53
+ @JRubyMethod(name = "y")
54
+ public IRubyObject y(ThreadContext context) {
55
+ Ruby ruby = context.getRuntime();
56
+ return ruby.newFloat(rect.getY());
57
+ }
58
+
59
+
60
+ @JRubyMethod(name = "width")
61
+ public IRubyObject widht(ThreadContext context) {
62
+ Ruby ruby = context.getRuntime();
63
+ return ruby.newFloat(rect.getWidth());
64
+ }
65
+
66
+
67
+ @JRubyMethod(name = "height")
68
+ public IRubyObject height(ThreadContext context) {
69
+ Ruby ruby = context.getRuntime();
70
+ return ruby.newFloat(rect.getHeight());
71
+ }
72
+
73
+ @JRubyMethod(name = "==")
74
+ public IRubyObject r_eql(ThreadContext context, IRubyObject rRect) {
75
+ Ruby ruby = context.getRuntime();
76
+
77
+
78
+ return ruby.newBoolean(rect.equals(((RRect) rRect).getJava()));
79
+ }
80
+
81
+ @JRubyMethod(name = "contains_point?")
82
+ public IRubyObject containsPoint(ThreadContext context, IRubyObject point) {
83
+ Ruby ruby = context.getRuntime();
84
+ boolean result = this.rect.containsPoint(((RPoint)point).getJava());
85
+
86
+ return ruby.newBoolean(result);
87
+ }
88
+
89
+ @JRubyMethod(name = "inspect")
90
+ public IRubyObject inspect(ThreadContext context) {
91
+ Ruby ruby = context.getRuntime();
92
+ String str = String.format("[%s,%s],[%s,%s]", rect.getX(),rect.getY(),rect.getX()+rect.getWidth(),rect.getY()+rect.getHeight());
93
+ return ruby.newString(str);
94
+ }
95
+
96
+ }
@@ -0,0 +1,49 @@
1
+ package geometry;
2
+
3
+ public class Rect {
4
+
5
+ double x;
6
+ double y;
7
+ double width;
8
+ double height;
9
+
10
+ public Rect(double x, double y, double width, double height) {
11
+ this.x = x;
12
+ this.y = y;
13
+ this.width = width;
14
+ this.height = height;
15
+ }
16
+
17
+ public boolean containsPoint(Point point) {
18
+ return point.getX() >= this.x && point.getY() >= this.y &&
19
+ point.getX() <= (this.x + this.width) && point.getY() <= (this.y + this.height);
20
+ }
21
+
22
+ public double getX() {
23
+ return this.x;
24
+ }
25
+
26
+ public double getY() {
27
+ return this.y;
28
+ }
29
+
30
+ public double getWidth() {
31
+ return this.width;
32
+ }
33
+
34
+ public double getHeight() {
35
+ return this.height;
36
+ }
37
+
38
+ @Override public boolean equals(Object other) {
39
+ boolean result = false;
40
+ if (other instanceof Rect) {
41
+ Rect rect = (Rect) other;
42
+ result = (this.x == rect.getX() && this.y == rect.getY() &&
43
+ this.width == rect.getWidth() && this.height == rect.getHeight());
44
+ }
45
+ return result;
46
+ }
47
+
48
+ }
49
+
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+ extension_name = 'geometry'
3
+ dir_config(extension_name)
4
+ create_makefile(extension_name)
5
+
@@ -0,0 +1,23 @@
1
+ require "json"
2
+
3
+ module LocalGeocoder
4
+ DATA_DIR = "data"
5
+ end
6
+
7
+ require "local_geocoder/version"
8
+ require "local_geocoder/data_source"
9
+ require "local_geocoder/geocoder"
10
+
11
+ if RUBY_PLATFORM =~ /java/
12
+ require 'jruby'
13
+ require 'geometry.jar'
14
+
15
+ java_import 'geometry.GeometryService'
16
+
17
+ Java::Geometry::GeometryService.new.basicLoad(JRuby.runtime)
18
+ else
19
+ require "local_geocoder/geometry/point"
20
+ require "local_geocoder/geometry/rect"
21
+ require "local_geocoder/geometry/polygon"
22
+ end
23
+
@@ -0,0 +1,57 @@
1
+ module LocalGeocoder
2
+
3
+ Entity = Struct.new(:short_name, :long_name, :geometries) do
4
+ def inspect
5
+ "#{self.long_name}#{self.short_name.empty? ? "" : ' ('+self.short_name+')' }"
6
+ end
7
+ end
8
+
9
+ class DataSource
10
+
11
+ def initialize(dir)
12
+ @countries = load_data(dir, "countries/features.geo.json")
13
+ @administrative_areas_level_1 = Hash.new do |h,k|
14
+ h[k] = load_data(File.join(dir, "countries", k), "features.geo.json")
15
+ end
16
+ @administrative_areas_level_2 = Hash.new do |h,k|
17
+ h[k] = load_data(File.join(dir, "countries", *k), "features.geo.json")
18
+ end
19
+ end
20
+
21
+ def countries
22
+ @countries
23
+ end
24
+
25
+ def administrative_areas_level_1(country_id)
26
+ return nil if country_id.nil?
27
+ @administrative_areas_level_1[country_id]
28
+ end
29
+
30
+ def administrative_areas_level_2(country_id, aa1_id)
31
+ return nil if country_id.nil? || aa1_id.nil?
32
+ @administrative_areas_level_2[[country_id, aa1_id]]
33
+ end
34
+
35
+ private
36
+
37
+ def load_data(dir, file)
38
+ features = JSON.load(File.open(File.join(dir, file)))['features']
39
+ features.map do |f|
40
+ id = f['id'] ? f['id'][/\w+$/] : ""
41
+
42
+ # Note: Perimeter is always first element in GeoJSON.
43
+ geometries = case f['geometry']['type']
44
+ when "MultiPolygon"
45
+ f['geometry']['coordinates'].map { |g| Geometry::Polygon.from_point_array(g.first) }
46
+ when "Polygon"
47
+ Array(Geometry::Polygon.from_point_array(f['geometry']['coordinates'].first))
48
+ else
49
+ raise "Don't know how to handle geometry type: #{f['geometry']['type']}"
50
+ end
51
+ Entity.new(id, f['properties']['name'], geometries)
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,48 @@
1
+ module LocalGeocoder
2
+
3
+ Result = Struct.new(:country, :administrative_area_level_1, :administrative_area_level_2) do
4
+ alias_method :state, :administrative_area_level_1
5
+ alias_method :county, :administrative_area_level_2
6
+ def inspect
7
+ "#{self.country.inspect}, #{self.administrative_area_level_1.inspect}, #{self.administrative_area_level_2.inspect}"
8
+ end
9
+ end
10
+
11
+ class Geocoder
12
+
13
+ def initialize
14
+ @data_source = DataSource.new(DATA_DIR)
15
+ end
16
+
17
+ def reverse_geocode(lng, lat)
18
+ country = find_result(lng, lat)
19
+ end
20
+
21
+ private
22
+
23
+ def find_result(lng, lat)
24
+ cnt = find_country(lng, lat)
25
+ aa1, aa2 = find_administrative_areas(cnt.short_name, lng, lat) if cnt && cnt.short_name == "USA"
26
+ Result.new(cnt, aa1, aa2)
27
+ end
28
+
29
+ def find_country(lng, lat)
30
+ @data_source.countries.find { |c| contains_location?(c, lng, lat); }
31
+ end
32
+
33
+ def find_administrative_areas(country_id, lng, lat)
34
+ aa1 = @data_source.administrative_areas_level_1(country_id).find { |a| contains_location?(a, lng, lat) }
35
+ return nil,nil if aa1.nil?
36
+
37
+ aa2 = @data_source.administrative_areas_level_2(country_id, aa1.short_name).find { |a| contains_location?(a, lng, lat) }
38
+ return aa1, aa2
39
+ end
40
+
41
+ def contains_location?(entity, lng, lat)
42
+ entity.geometries.any? { |g| g.contains_point?(Geometry::Point.new(lng, lat)) }
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+
@@ -0,0 +1,18 @@
1
+ module LocalGeocoder
2
+ module Geometry
3
+
4
+ class Point
5
+ attr_accessor :x, :y, :data
6
+ def initialize(x, y, data=nil)
7
+ @x = x
8
+ @y = y
9
+ @data = data
10
+ end
11
+
12
+ def inspect
13
+ "[#{x}, #{y}]"
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,69 @@
1
+ module LocalGeocoder
2
+ module Geometry
3
+
4
+ class Polygon
5
+ attr_accessor :points
6
+
7
+ def initialize(points)
8
+ @points = points
9
+ end
10
+
11
+ def self.from_point_array(points)
12
+ Polygon.new(points.map { |p| Point.new(*p) })
13
+ end
14
+
15
+ def number_of_points
16
+ @points.size
17
+ end
18
+
19
+ def [](i)
20
+ @points[i]
21
+ end
22
+
23
+ def bounding_box
24
+ @bounding_box ||= begin
25
+ min_x, max_x = @points.minmax_by { |p| p.x }.map { |p| p.x }
26
+ min_y, max_y = @points.minmax_by { |p| p.y }.map { |p| p.y }
27
+ Rect.new(min_x, min_y, max_x-min_x, max_y-min_y)
28
+ end
29
+ end
30
+
31
+ def contains_point?(point)
32
+ return false if !self.bounding_box.contains_point?(point)
33
+
34
+ contains_point = false
35
+ i = -1
36
+ j = self.number_of_points - 1
37
+ while (i += 1) < self.number_of_points
38
+ p1 = self[i]; p2 = self[j]
39
+ if within_y_bounds?(point, p1, p2)
40
+ if intersects_line_segment?(point, p1, p2)
41
+ contains_point = !contains_point
42
+ end
43
+ end
44
+ j = i
45
+ end
46
+ return contains_point
47
+ end
48
+
49
+ def inspect
50
+ @points.map { |p| p.inspect }.join(",")
51
+ end
52
+
53
+ private
54
+
55
+ def within_y_bounds?(point, p1, p2)
56
+ (p1.y <= point.y && point.y < p2.y) ||
57
+ (p2.y <= point.y && point.y < p1.y)
58
+ end
59
+
60
+ def intersects_line_segment?(point, p1, p2)
61
+ (point.x < (p2.x - p1.x) * (point.y - p1.y) /
62
+ (p2.y - p1.y) + p1.x)
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+ end
69
+
@@ -0,0 +1,22 @@
1
+ module LocalGeocoder
2
+ module Geometry
3
+
4
+ Rect = Struct.new(:x, :y, :width, :height) do
5
+ def contains_point?(point)
6
+ point.x >= self.x && point.y >= self.y &&
7
+ point.x <= (self.x+self.width) && point.y <= (self.y+self.height)
8
+ end
9
+
10
+ def ==(rect)
11
+ self.x == rect.x && self.y == rect.y &&
12
+ self.width == rect.width && self.height == rect.height
13
+ end
14
+
15
+ def inspect
16
+ "[#{x},#{y}],[#{x+width},#{y+height}]"
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module LocalGeocoder
2
+ VERSION = '0.1.1'
3
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: local-geocoder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Aish Fenton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: trollop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake-compiler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ executables:
72
+ - local_geocode
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ext/geometry/GeometryService.java
77
+ - ext/geometry/Point.java
78
+ - ext/geometry/Polygon.java
79
+ - ext/geometry/Rect.java
80
+ - ext/geometry/RPoint.java
81
+ - ext/geometry/RPolygon.java
82
+ - ext/geometry/RRect.java
83
+ - ext/geometry/extconf.rb
84
+ - lib/local_geocoder/data_source.rb
85
+ - lib/local_geocoder/geocoder.rb
86
+ - lib/local_geocoder/geometry/point.rb
87
+ - lib/local_geocoder/geometry/polygon.rb
88
+ - lib/local_geocoder/geometry/rect.rb
89
+ - lib/local_geocoder/version.rb
90
+ - lib/local_geocoder.rb
91
+ - bin/local_geocode
92
+ homepage:
93
+ licenses: []
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.0.3
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Reverse geocodes lng, lat pairs into country codes (plus State and Counties
115
+ within the US). Runs locally, with no external dependancies, and is fast enough
116
+ for large batch jobs
117
+ test_files: []