simple_mercator_location 1.0.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,137 @@
1
+ class SimpleMercatorLocation
2
+
3
+ attr_accessor :lat_deg, :lon_deg, :zoom
4
+
5
+ def initialize(*args)
6
+ @zoom = 2
7
+ @lat_deg = 0
8
+ @lon_deg = 0
9
+
10
+ if args.first.is_a?(Hash)
11
+ @lat_deg = args.first[:lat] if args.first.has_key?(:lat)
12
+ @lon_deg = args.first[:lon] if args.first.has_key?(:lon)
13
+ @zoom = args.first[:zoom] if args.first.has_key?(:zoom)
14
+ end
15
+ end
16
+
17
+
18
+ # latitude in radiants
19
+ def lat_rad
20
+ shift_to_rad * lat_deg
21
+ end
22
+
23
+
24
+ # longitude in radiants
25
+ def lon_rad
26
+ shift_to_rad * lon_deg
27
+ end
28
+
29
+
30
+ # factor for scaling to radiants
31
+ def shift_to_rad
32
+ Rational(Math::PI, 180)
33
+ end
34
+
35
+
36
+ # factor for scaling to degrees
37
+ def shift_to_deg
38
+ Rational(180.0, Math::PI)
39
+ end
40
+
41
+
42
+ # size of our tiles
43
+ def tile_size
44
+ 256
45
+ end
46
+
47
+
48
+ # earth radius in meters
49
+ def earth_radius
50
+ 6378137
51
+ end
52
+
53
+
54
+ # set the zoom-level and returns self
55
+ def zoom_at(scale)
56
+ @zoom = scale
57
+ self
58
+ end
59
+
60
+
61
+ # origin of google map lieves on top-left. For getting pixels,
62
+ # we assume displaying the earh an zoomm-level 0 and a square tile
63
+ # of tile_size * tile_size pixels. The origin of our wsg84 system
64
+ # (lat, lon = (0,0) sits and (128,128)px.
65
+ def origin_px
66
+ [tile_size / 2, tile_size / 2]
67
+ end
68
+
69
+
70
+ # return the pixels per degree
71
+ def pixel_per_degree
72
+ Rational(tile_size, 360)
73
+ end
74
+
75
+
76
+ # return the pixels per radiant
77
+ def pixel_per_rad
78
+ tile_size / (2 * Math::PI) # tile_size / (360 * shift_to_rad)
79
+ end
80
+
81
+
82
+ # scale the latitude via mercator projections
83
+ # see http://en.wikipedia.org/wiki/Mercator_projection#Derivation_of_the_Mercator_projection
84
+ # return the scaled latitude as radiant
85
+ def lat_scaled_rad
86
+ Math.log( Math.tan( Rational(Math::PI,4) + Rational(lat_rad,2) ))
87
+ end
88
+
89
+
90
+ # returns the scaled latitude as degrees
91
+ def lat_scaled_deg
92
+ lat_scaled_rad * shift_to_deg
93
+ end
94
+
95
+
96
+ # returns the mercotors meters count for the location
97
+ def to_m
98
+ mx = earth_radius * lon_rad
99
+ my = earth_radius * lat_scaled_rad
100
+
101
+ my = (my.round(8) == 0)? 0 : my
102
+
103
+ [mx.to_f, my.to_f]
104
+ end
105
+
106
+
107
+ # calculates the google world-coordinates as described here:
108
+ # https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
109
+ def to_w
110
+ px = origin_px.first + pixel_per_rad * lon_rad
111
+ py = origin_px.last - pixel_per_rad * lat_scaled_rad
112
+
113
+ return [px, py]
114
+ end
115
+
116
+
117
+ # returns the pixel coordinates at the given zoom level
118
+ def to_px
119
+ tiles_count = 2**zoom
120
+ wx,wy = self.to_w
121
+ px = wx * tiles_count
122
+ py = wy * tiles_count
123
+ return [px.to_i, py.to_i]
124
+ end
125
+
126
+
127
+ # calculates the tile numbers for google maps at the given zoom level
128
+ def to_tile
129
+ px,py = self.to_px
130
+
131
+ tx = Rational(px, tile_size).to_i
132
+ ty = Rational(py, tile_size).to_i
133
+
134
+ return [tx,ty]
135
+ end
136
+
137
+ end
@@ -0,0 +1,130 @@
1
+ require "spec_helper"
2
+
3
+ describe SimpleMercatorLocation do
4
+
5
+ describe "accessors" do
6
+
7
+ let(:loc) { SimpleMercatorLocation.new }
8
+
9
+ attrs = [:lat_deg, :lon_deg, :zoom]
10
+
11
+ attrs.each do |attr|
12
+ it "should have getter and setter for #{attr}" do
13
+ loc.should respond_to("#{attr}".to_sym)
14
+ loc.should respond_to("#{attr}=".to_sym)
15
+ loc.send("#{attr}=".to_sym, "asd")
16
+ loc.send("#{attr}".to_sym).should eql "asd"
17
+ end
18
+ end
19
+ end
20
+
21
+
22
+ describe "#initialize" do
23
+ context "without args" do
24
+
25
+ let(:loc) { SimpleMercatorLocation.new }
26
+
27
+ it "returns a location object" do
28
+ loc.should be_an_instance_of(SimpleMercatorLocation)
29
+ end
30
+ end
31
+
32
+
33
+ context "given a hash with lat, lon and zoom" do
34
+ let(:loc) { SimpleMercatorLocation.new(lat: 1, lon: 2, zoom: 12) }
35
+
36
+ it "returns a location object" do
37
+ loc.should be_an_instance_of(SimpleMercatorLocation)
38
+ end
39
+
40
+ it "return a location object with lat and lon set" do
41
+ loc.lat_deg.should eql 1
42
+ loc.lon_deg.should eql 2
43
+ loc.zoom.should eql 12
44
+ end
45
+ end
46
+ end
47
+
48
+
49
+ describe "#zoom_at" do
50
+ let(:loc) { SimpleMercatorLocation.new }
51
+ it "sets the zoom scale and returns self" do
52
+ loc.zoom_at(15).should eql loc
53
+ loc.zoom_at(15).zoom.should eql 15
54
+ end
55
+ end
56
+
57
+ describe "to_m" do
58
+ context "given no args (so using lat = 0 and lon = 0)" do
59
+ let(:loc) { SimpleMercatorLocation.new }
60
+ it "should return origin" do
61
+ loc.to_m.should eql [0.0,0.0]
62
+ end
63
+ end
64
+
65
+ context "given a location" do
66
+ places =
67
+ [
68
+ { lat: 48.16608541901253, lon: 11.6455078125, mx: 1296371.99971659, my: 6134530.14205511 },
69
+ { lat: 48.10743118848038, lon: 11.42578125, mx: 1271912.15066533, my: 6124746.20243460 },
70
+ { lat: 48.22467264956519, lon: 12.12890625, mx: 1350183.66762935, my: 6144314.08167561 },
71
+ { lat: 0, lon: 0, mx: 0.0, my: 0.0 },
72
+ ]
73
+ places.each do |place|
74
+ it "calculates the mercator projection of (lat: #{place[:lat]}, lon: #{place[:lon]}) to (meters x: #{place[:mx]}, meters y: #{place[:my]})" do
75
+ meters = SimpleMercatorLocation.new(lon: place[:lon], lat: place[:lat]).to_m
76
+ meters.map!{|m| m.round(8) }.should eql([place[:mx], place[:my]])
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ describe "to_w" do
83
+ places =
84
+ [
85
+ { lat: 41.850033, lon: -87.65005229999997, wx: 65.67107392000001, wy: 95.1748950436046 },
86
+ #{ lat: 48.10743118848038, lon: 11.42578125, wx: 1271912.15066533, wy: 6124746.20243460 },
87
+ #{ lat: 48.22467264956519, lon: 12.12890625, wx: 1350183.66762935, wy: 6144314.08167561 },
88
+ #{ lat: 0, lon: 0, wx: 0.0, wy: 0.0 },
89
+ ]
90
+ places.each do |place|
91
+ it "calculates the mercator projection of (lat: #{place[:lat]}, lon: #{place[:lon]}) to (world coordinate x: #{place[:wx]}, world coordinate y: #{place[:wy]})" do
92
+ SimpleMercatorLocation.new(lon: place[:lon], lat: place[:lat]).to_w.should eql([place[:wx], place[:wy]])
93
+ end
94
+ end
95
+
96
+
97
+ end
98
+
99
+
100
+ describe "to_px" do
101
+ places =
102
+ [
103
+ { zoom: 11, lat: 49.38237278700955, lon: 8.61328125, px: 274688, py: 179200 },
104
+ { zoom: 14, lat: 49.38237278700955, lon: 8.61328125, px: 2197504, py: 1433600 },
105
+ ]
106
+
107
+ places.each do |place|
108
+ it "calculates the pixels of (lat: #{place[:lat]}, lon: #{place[:lon]}) to (px: #{place[:px]}, py: #{place[:py]})" do
109
+ SimpleMercatorLocation.new(lon: place[:lon], lat: place[:lat], zoom: place[:zoom]).to_px.should eql([place[:px], place[:py]])
110
+ end
111
+ end
112
+ end
113
+
114
+
115
+ describe "to_tile" do
116
+ places =
117
+ [
118
+ { zoom: 11, lat: 49.412758, lon: 8.671938, tx: 1073, ty: 699 },
119
+ { zoom: 11, lat: 40.689359, lon: -74.045197, tx: 602, ty: 770 },
120
+ { zoom: 15, lat: 40.689359, lon: -74.045197, tx: 9644, ty: 12322 },
121
+ ]
122
+
123
+ places.each do |place|
124
+ it "calculates the tile of (lat: #{place[:lat]}, lon: #{place[:lon]}) to (tile x: #{place[:tx]}, tile y: #{place[:ty]})" do
125
+ SimpleMercatorLocation.new(lon: place[:lon], lat: place[:lat], zoom: place[:zoom]).to_tile.should eql([place[:tx], place[:ty]])
126
+ end
127
+ end
128
+ end
129
+
130
+ end
@@ -0,0 +1,19 @@
1
+ require "rspec"
2
+ require "awesome_print"
3
+
4
+ RSpec.configure do |config|
5
+ config.color_enabled = true
6
+ config.filter_run :focus => true
7
+ config.run_all_when_everything_filtered = true
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ end
10
+
11
+ def timed(name)
12
+ start = Time.now
13
+ puts "\n[STARTED: #{name}]"
14
+ yield if block_given?
15
+ finish = Time.now
16
+ puts "[FINISHED: #{name} in #{(finish - start) * 1000} milliseconds]"
17
+ end
18
+
19
+ require "simple_mercator_location"
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple_mercator_location
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Roman Lehnert
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.5'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.5'
30
+ description: Converts WSG84 Coordinates via Mercator-projection to meters and tiles
31
+ email: roman.lehnert@googlemail.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - lib/simple_mercator_location.rb
37
+ - spec/lib/simple_mercator_location_spec.rb
38
+ - spec/spec_helper.rb
39
+ homepage: https://github.com/romanlehnert/simple_mercator_location
40
+ licenses:
41
+ - MIT
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 1.8.25
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: A tiny lib for the mercator projecton
64
+ test_files:
65
+ - spec/lib/simple_mercator_location_spec.rb
66
+ - spec/spec_helper.rb