local-geocoder 0.1.1

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,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: []