geodelta 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 39c605312c9e7f3b221e430a07a1e464c4adc79f
4
+ data.tar.gz: 0134bef761ed3b2b298d28393ae7ea5ff51e551c
5
+ SHA512:
6
+ metadata.gz: 236ca2f94b217453f6aa030a4d1756686ce3a1a71a41bd1aeaff6eabb4821d36e22bcfd9988e8a93b52caebefcc501059746da1df42d5f32dfb332189fee9d9e
7
+ data.tar.gz: c7ce33335afd0b74cdaa2a0819f2c6f8b879da52f94cd10649412ec55423a5e190758a6eb81aceb656a51ffa401cc844c0858370f8742726b91bae194e525052
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .ruby-version
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+
2
+ source "https://rubygems.org"
3
+
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Yuya Kato (Nayutaya Inc.)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,60 @@
1
+ # geodelta-ruby
2
+
3
+ [![Code Climate](https://codeclimate.com/github/nayutaya/geodelta-ruby/badges/gpa.svg)](https://codeclimate.com/github/nayutaya/geodelta-ruby)
4
+ [![Test Coverage](https://codeclimate.com/github/nayutaya/geodelta-ruby/badges/coverage.svg)](https://codeclimate.com/github/nayutaya/geodelta-ruby/coverage)
5
+ [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nayutaya/geodelta-ruby/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/nayutaya/geodelta-ruby/?branch=master)
6
+ [![Circle CI](https://circleci.com/gh/nayutaya/geodelta-ruby.svg?style=shield)](https://circleci.com/gh/nayutaya/geodelta-ruby)
7
+ [![Gem Version](https://badge.fury.io/rb/geodelta.svg)](http://badge.fury.io/rb/geodelta)
8
+
9
+ An implementation of GeoDelta for Ruby.
10
+
11
+ ## Overview
12
+
13
+ TODO
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem "geodelta"
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ $ bundle
26
+
27
+ Or install it yourself as:
28
+
29
+ $ gem install geodelta
30
+
31
+ ## Usage
32
+
33
+ TODO
34
+
35
+ ## Development
36
+
37
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
38
+
39
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it ( https://github.com/nayutaya/geodelta-ruby/fork )
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create a new Pull Request
48
+
49
+ ## Author
50
+
51
+ Yuya Kato (Nayutaya Inc.) <yuyakato@gmail.com>
52
+
53
+ ## License
54
+
55
+ MIT License
56
+
57
+ ## Other languages
58
+
59
+ * JavaScript: https://github.com/nayutaya/geodelta-js
60
+ * Java: https://github.com/nayutaya/geodelta
@@ -0,0 +1,9 @@
1
+
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ task :default => :test
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.pattern = "test/*_test.rb"
9
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "geodelta"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,3 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.2.0
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "geodelta/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "geodelta"
8
+ spec.version = GeoDelta::VERSION
9
+ spec.authors = ["Yuya Kato"]
10
+ spec.email = ["yuyakato@gmail.com"]
11
+
12
+ spec.summary = %q{An implementation of GeoDelta for Ruby.}
13
+ spec.description = %q{An implementation of GeoDelta for Ruby.}
14
+ spec.homepage = "https://github.com/nayutaya/geodelta-ruby"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.9"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "minitest"
25
+ spec.add_development_dependency "codeclimate-test-reporter"
26
+ end
@@ -0,0 +1,4 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative "./geodelta/version"
4
+ require_relative "./geodelta/core"
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative "./projector"
4
+ require_relative "./delta_geometry"
5
+ require_relative "./encoder"
6
+
7
+ module GeoDelta
8
+ def self.get_delta_ids(lat, lng, level)
9
+ nx, ny = GeoDelta::Projector.latlng_to_nxy(lat, lng)
10
+ return GeoDelta::DeltaGeometry.get_delta_ids(nx, ny, level)
11
+ end
12
+
13
+ def self.get_delta_code(lat, lng, level)
14
+ ids = self.get_delta_ids(lat, lng, level)
15
+ return GeoDelta::Encoder.encode(ids)
16
+ end
17
+
18
+ def self.get_center_from_delta_ids(ids)
19
+ nx, ny = GeoDelta::DeltaGeometry.get_center(ids)
20
+ return GeoDelta::Projector.nxy_to_latlng(nx, ny)
21
+ end
22
+
23
+ def self.get_center_from_delta_code(code)
24
+ ids = GeoDelta::Encoder.decode(code)
25
+ return self.get_center_from_delta_ids(ids)
26
+ end
27
+
28
+ def self.get_coordinates_from_ids(ids)
29
+ return GeoDelta::DeltaGeometry.get_coordinates(ids).
30
+ map { |nx, ny| GeoDelta::Projector.nxy_to_latlng(nx, ny) }
31
+ end
32
+
33
+ def self.get_coordinates_from_code(code)
34
+ ids = GeoDelta::Encoder.decode(code)
35
+ return self.get_coordinates_from_ids(ids)
36
+ end
37
+ end
@@ -0,0 +1,180 @@
1
+ # encoding: utf-8
2
+
3
+ module GeoDelta
4
+ module DeltaGeometry
5
+ # 指定された座標(x,y)に該当するワールドデルタの番号を返す
6
+ # ただし、-12.0 <= x <= +12.0、-12.0 <= y <= +12.0
7
+ def self.get_world_delta_id(x, y)
8
+ xx = x % 24
9
+ yy = y.abs
10
+ return (y >= 0.0 ? 0 : 4) +
11
+ case
12
+ when yy >= +2.0 * (xx - 0.0) then 0
13
+ when yy <= -2.0 * (xx - 12.0) then 1
14
+ when yy >= +2.0 * (xx - 12.0) then 2
15
+ when yy <= -2.0 * (xx - 24.0) then 3
16
+ else 0
17
+ end
18
+ end
19
+
20
+ # 指定された座標(x,y)に該当する上向きのサブデルタの番号を返す
21
+ # ただし、0.0 <= x <= +12.0、0.0 <= y <= +12.0
22
+ def self.get_upper_delta_id(x, y)
23
+ return 3 if y < -2.0 * (x - 6.0)
24
+ return 2 if y < +2.0 * (x - 6.0)
25
+ return 1 if y > 6.0
26
+ return 0
27
+ end
28
+
29
+ # 指定された座標(x,y)に該当する下向きのサブデルタの番号を返す
30
+ # ただし、0.0 <= x <= +12.0、0.0 <= y <= +12.0
31
+ def self.get_lower_delta_id(x, y)
32
+ return 3 if y > -2.0 * (x - 12.0)
33
+ return 2 if y > +2.0 * x
34
+ return 1 if y < 6.0
35
+ return 0
36
+ end
37
+
38
+ # 指定されたワールドデルタが上向きかどうかを返す
39
+ def self.upper_world_delta?(id)
40
+ return (id % 2 == (id < 4 ? 1 : 0))
41
+ end
42
+
43
+ # 指定されたサブデルタが上向きかどうか返す
44
+ def self.upper_sub_delta?(parent_is_upper, id)
45
+ return (parent_is_upper ? (id != 0) : (id == 0))
46
+ end
47
+
48
+ def self.upper_delta?(ids)
49
+ return ids.inject(nil) { |upper, id|
50
+ if upper.nil?
51
+ self.upper_world_delta?(id)
52
+ else
53
+ self.upper_sub_delta?(upper, id)
54
+ end
55
+ }
56
+ end
57
+
58
+ def self.transform_world_delta(id, x, y)
59
+ xx = (x + [+6.0, +0.0, -6.0, -12.0, +6.0, +0.0, -6.0, -12.0][id]) % 12
60
+ yy = (y + [+0.0, +0.0, +0.0, +0.0, +12.0, +12.0, +12.0, +12.0][id]) % 12
61
+ return [xx, yy]
62
+ end
63
+
64
+ def self.transform_upper_delta(id, x, y)
65
+ xx = (x + [-3.0, -3.0, -6.0, -0.0][id]) * 2
66
+ yy = (y + [-0.0, -6.0, -0.0, -0.0][id]) * 2
67
+ return [xx, yy]
68
+ end
69
+
70
+ def self.transform_lower_delta(id, x, y)
71
+ xx = (x + [-3.0, -3.0, -0.0, -6.0][id]) * 2
72
+ yy = (y + [-6.0, -0.0, -6.0, -6.0][id]) * 2
73
+ return [xx, yy]
74
+ end
75
+
76
+ def self.get_delta_ids(x, y, level)
77
+ ids = [self.get_world_delta_id(x, y)]
78
+ xx, yy = self.transform_world_delta(ids.last, x, y)
79
+ upper = self.upper_world_delta?(ids.last)
80
+
81
+ (level - 1).times {
82
+ if upper
83
+ ids << self.get_upper_delta_id(xx, yy)
84
+ xx, yy = self.transform_upper_delta(ids.last, xx, yy)
85
+ upper = self.upper_sub_delta?(upper, ids.last)
86
+ else
87
+ ids << self.get_lower_delta_id(xx, yy)
88
+ xx, yy = self.transform_lower_delta(ids.last, xx, yy)
89
+ upper = self.upper_sub_delta?(upper, ids.last)
90
+ end
91
+ }
92
+
93
+ return ids
94
+ end
95
+
96
+ def self.get_world_delta_center(id)
97
+ case id
98
+ when 0 then [ +0.0, +8.0]
99
+ when 1 then [ +6.0, +4.0]
100
+ when 2 then [+12.0, +8.0]
101
+ when 3 then [+18.0, +4.0]
102
+ when 4 then [ +0.0, -8.0]
103
+ when 5 then [ +6.0, -4.0]
104
+ when 6 then [+12.0, -8.0]
105
+ when 7 then [+18.0, -4.0]
106
+ end
107
+ end
108
+
109
+ def self.get_upper_sub_delta_distance(id)
110
+ case id
111
+ when 0 then [+0.0, +0.0]
112
+ when 1 then [+0.0, +4.0]
113
+ when 2 then [+3.0, -2.0]
114
+ when 3 then [-3.0, -2.0]
115
+ end
116
+ end
117
+
118
+ def self.get_lower_sub_delta_distance(id)
119
+ case id
120
+ when 0 then [+0.0, +0.0]
121
+ when 1 then [+0.0, -4.0]
122
+ when 2 then [-3.0, +2.0]
123
+ when 3 then [+3.0, +2.0]
124
+ end
125
+ end
126
+
127
+ def self.get_sub_delta_distance(parent_is_upper, id)
128
+ if parent_is_upper
129
+ return self.get_upper_sub_delta_distance(id)
130
+ else
131
+ return self.get_lower_sub_delta_distance(id)
132
+ end
133
+ end
134
+
135
+ def self.get_center(ids)
136
+ xs, ys = [], []
137
+ upper = nil
138
+
139
+ ids.each_with_index { |id, index|
140
+ if index == 0
141
+ x, y = self.get_world_delta_center(id)
142
+ upper = self.upper_world_delta?(id)
143
+ xs << x
144
+ ys << y
145
+ else
146
+ x, y = self.get_sub_delta_distance(upper, id)
147
+ upper = self.upper_sub_delta?(upper, id)
148
+ xs << (x / (2 ** (index - 1)))
149
+ ys << (y / (2 ** (index - 1)))
150
+ end
151
+ }
152
+
153
+ x = xs.sort.inject(0.0, &:+)
154
+ y = ys.sort.inject(0.0, &:+)
155
+
156
+ x -= 24.0 if x > 12.0
157
+
158
+ return [x, y]
159
+ end
160
+
161
+ def self.get_coordinates(ids)
162
+ cx, cy = self.get_center(ids)
163
+ level = ids.size
164
+ sign = (self.upper_delta?(ids) ? +1 : -1)
165
+ scale = 1.0 / (2 ** (level - 1)) * sign
166
+
167
+ dx1 = 0.0
168
+ dy1 = 8.0 * scale
169
+ dx2 = 6.0 * scale
170
+ dy2 = 4.0 * scale
171
+
172
+ return [
173
+ [cx, cy ],
174
+ [cx + dx1, cy + dy1],
175
+ [cx + dx2, cy - dy2],
176
+ [cx - dx2, cy - dy2],
177
+ ]
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,79 @@
1
+ # encoding: utf-8
2
+
3
+ module GeoDelta
4
+ module Encoder
5
+ WORLD_DELTA_TABLE = [
6
+ [0, "Z"],
7
+ [1, "Y"],
8
+ [2, "X"],
9
+ [3, "W"],
10
+ [4, "V"],
11
+ [5, "T"],
12
+ [6, "S"],
13
+ [7, "R"],
14
+ ].each(&:freeze).freeze
15
+ WORLD_ID_TO_CHAR = WORLD_DELTA_TABLE.inject({}) { |memo, (num, char)| memo[num] = char; memo }.freeze
16
+ WORLD_CHAR_TO_ID = WORLD_DELTA_TABLE.inject({}) { |memo, (num, char)| memo[char] = num; memo }.freeze
17
+
18
+ SUB_DELTA_TABLE = [
19
+ [[0, 0], "2"],
20
+ [[0, 1], "3"],
21
+ [[0, 2], "4"],
22
+ [[0, 3], "5"],
23
+ [[1, 0], "6"],
24
+ [[1, 1], "7"],
25
+ [[1, 2], "8"],
26
+ [[1, 3], "A"],
27
+ [[2, 0], "B"],
28
+ [[2, 1], "C"],
29
+ [[2, 2], "D"],
30
+ [[2, 3], "E"],
31
+ [[3, 0], "F"],
32
+ [[3, 1], "G"],
33
+ [[3, 2], "H"],
34
+ [[3, 3], "J"],
35
+ [[0] , "K"],
36
+ [[1] , "M"],
37
+ [[2] , "N"],
38
+ [[3] , "P"],
39
+ ].each(&:freeze).freeze
40
+ SUB_IDS_TO_CHAR = SUB_DELTA_TABLE.inject({}) { |memo, (nums, char)| memo[nums] = char; memo }.freeze
41
+ SUB_CHAR_TO_IDS = SUB_DELTA_TABLE.inject({}) { |memo, (nums, char)| memo[char] = nums; memo }.freeze
42
+
43
+ def self.encode_world_delta(id)
44
+ return WORLD_ID_TO_CHAR[id] || raise("invalid world delta id -- #{id}")
45
+ end
46
+
47
+ def self.decode_world_delta(code)
48
+ return WORLD_CHAR_TO_ID[code] || raise("invalid world delta code -- #{code}")
49
+ end
50
+
51
+ def self.encode_sub_delta(ids)
52
+ raise("sub delta ids is empty") if ids.empty?
53
+ return ids.each_slice(2).map { |part|
54
+ SUB_IDS_TO_CHAR[part] || raise("invalid sub delta ids -- #{part}")
55
+ }.join("")
56
+ end
57
+
58
+ def self.decode_sub_delta(codes)
59
+ raise("sub delta codes is empty") if codes.empty?
60
+ return codes.chars.inject([]) { |memo, char|
61
+ memo + (SUB_CHAR_TO_IDS[char] || raise("invalid sub delta code -- #{char}"))
62
+ }
63
+ end
64
+
65
+ def self.encode(ids)
66
+ raise("delta ids is empty") if ids.empty?
67
+ result = self.encode_world_delta(ids[0])
68
+ result += self.encode_sub_delta(ids[1..-1]) if ids.size >= 2
69
+ return result
70
+ end
71
+
72
+ def self.decode(codes)
73
+ raise("delta codes is empty") if codes.empty?
74
+ result = [self.decode_world_delta(codes[0])]
75
+ result += self.decode_sub_delta(codes[1..-1]) if codes.size >= 2
76
+ return result
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative "./delta_geometry"
4
+
5
+ module GeoDelta
6
+ module HexGeometry
7
+ def self.get_hex_position(ids)
8
+ unit = get_unit(ids)
9
+ x, y = GeoDelta::DeltaGeometry.get_center(ids)
10
+ ix = (x / unit * 2.0).floor % 6
11
+ iy = (y / unit ).floor % 2
12
+
13
+ case [ix, iy]
14
+ when [0, 0] then return 0
15
+ when [0, 1] then return 3
16
+ when [1, 0] then return 1
17
+ when [1, 1] then return 2
18
+ when [2, 0] then return 4
19
+ when [2, 1] then return 5
20
+ when [3, 0] then return 3
21
+ when [3, 1] then return 0
22
+ when [4, 0] then return 2
23
+ when [4, 1] then return 1
24
+ when [5, 0] then return 5
25
+ when [5, 1] then return 4
26
+ else raise "BUG [#{i}, #{j}]"
27
+ end
28
+ end
29
+
30
+ def self.get_base_delta_ids(ids)
31
+ unit = get_unit(ids)
32
+ ux = unit / 2.0
33
+ uy = unit / 3.0
34
+ pos = self.get_hex_position(ids)
35
+ x, y = GeoDelta::DeltaGeometry.get_center(ids)
36
+
37
+ sx, sy =
38
+ case pos
39
+ when 0 then [0.0, 0.0 ]
40
+ when 1 then [-ux, +uy ]
41
+ when 2 then [-ux, +uy * 3]
42
+ when 3 then [0.0, +uy * 4]
43
+ when 4 then [+ux, +uy * 3]
44
+ when 5 then [+ux, +uy ]
45
+ else raise "BUG [#{pos}]"
46
+ end
47
+
48
+ return nil if x + sx > +12.0
49
+ return nil if x + sx < -12.0 + unit
50
+ return nil if y + sy > +12.0
51
+ return nil if y + sy < -12.0 + unit
52
+
53
+ return GeoDelta::DeltaGeometry.get_delta_ids(x + sx, y + sy, ids.size)
54
+ end
55
+
56
+ def self.get_part_delta_ids(base_ids)
57
+ level = base_ids.size
58
+ unit = get_unit(base_ids)
59
+ x, y = GeoDelta::DeltaGeometry.get_coordinates(base_ids)[1]
60
+
61
+ x1 = x - (unit / 2.0)
62
+ x2 = x
63
+ x3 = x + (unit / 2.0)
64
+
65
+ y1 = y + (unit * 2.0 / 3.0)
66
+ y2 = y + (unit / 3.0)
67
+ y3 = y - (unit / 3.0)
68
+ y4 = y - (unit * 2.0 / 3.0)
69
+
70
+ return [
71
+ [x2, y1],
72
+ [x3, y2],
73
+ [x3, y3],
74
+ [x2, y4],
75
+ [x1, y3],
76
+ [x1, y2],
77
+ ].map { |xx, yy|
78
+ GeoDelta::DeltaGeometry.get_delta_ids(xx, yy, level)
79
+ }
80
+ end
81
+
82
+ def self.get_coordinates(base_ids)
83
+ unit = get_unit(base_ids)
84
+ u1 = unit
85
+ u2 = unit / 2.0
86
+ x, y = GeoDelta::DeltaGeometry.get_coordinates(base_ids)[1]
87
+
88
+ return nil if y - u1 < -12.0
89
+
90
+ return [
91
+ [x , y ],
92
+ [x + u2, y + u1],
93
+ [x + u1, y ],
94
+ [x + u2, y - u1],
95
+ [x - u2, y - u1],
96
+ [x - u1, y ],
97
+ [x - u2, y + u1],
98
+ ]
99
+ end
100
+
101
+ def self.get_unit(ids)
102
+ level = ids.size
103
+ return 12.0 / (2 ** (level - 1))
104
+ end
105
+ private_class_method :get_unit
106
+ end
107
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative "./region"
4
+ require_relative "./hex_geometry"
5
+
6
+ module GeoDelta
7
+ module HexRegion
8
+ def self.get_base_delta_ids_in_region(x1, y1, x2, y2, level)
9
+ deltas = GeoDelta::Region.get_delta_ids_in_region(x1, y1, x2, y2, level)
10
+ return deltas.map { |ids| GeoDelta::HexGeometry.get_base_delta_ids(ids) }.compact.sort.uniq
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ module GeoDelta
4
+ module IdUtil
5
+ def self.get_all_delta_ids(level)
6
+ return (0..7).map { |id| [id] } if level == 1
7
+ return (0..7).to_a.product(*([(0..3).to_a] * (level - 1)))
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+
3
+ module GeoDelta
4
+ class Packer32
5
+ def pack_world_delta(id)
6
+ return id << 28
7
+ end
8
+
9
+ def unpack_world_delta(value)
10
+ return (value >> 28) & 0b111
11
+ end
12
+
13
+ def pack_sub_delta(level, id)
14
+ return id << (26 - ((level - 2) * 2))
15
+ end
16
+
17
+ def unpack_sub_delta(level, value)
18
+ return (value >> (26 - ((level - 2) * 2))) & 0b11
19
+ end
20
+
21
+ def pack_level(level)
22
+ return level
23
+ end
24
+
25
+ def unpack_level(value)
26
+ return value & 0b1111
27
+ end
28
+
29
+ def pack(ids)
30
+ wid = self.pack_world_delta(ids[0])
31
+ sids = ids[1..-1].each_with_index.map { |id, i| self.pack_sub_delta(i + 2, id) }.inject(0, &:+)
32
+ level = self.pack_level(ids.size)
33
+ return wid + sids + level
34
+ end
35
+
36
+ def unpack(value)
37
+ level = self.unpack_level(value)
38
+ wid = self.unpack_world_delta(value)
39
+ sids = (level - 1).times.map { |i| self.unpack_sub_delta(i + 2, value) }
40
+ return [wid] + sids
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+
3
+ module GeoDelta
4
+ class Packer64
5
+ def pack_world_delta(id)
6
+ return id << 59
7
+ end
8
+
9
+ def unpack_world_delta(value)
10
+ return (value >> 59) & 0b111
11
+ end
12
+
13
+ def pack_sub_delta(level, id)
14
+ return id << (57 - ((level - 2) * 2))
15
+ end
16
+
17
+ def unpack_sub_delta(level, value)
18
+ return (value >> (57 - ((level - 2) * 2))) & 0b11
19
+ end
20
+
21
+ def pack_level(level)
22
+ return level
23
+ end
24
+
25
+ def unpack_level(level)
26
+ return level & 0b11111
27
+ end
28
+
29
+ def pack(ids)
30
+ wid = self.pack_world_delta(ids[0])
31
+ sids = ids[1..-1].each_with_index.map { |id, i| self.pack_sub_delta(i + 2, id) }.inject(0, &:+)
32
+ level = self.pack_level(ids.size)
33
+ return wid + sids + level
34
+ end
35
+
36
+ def unpack(value)
37
+ level = self.unpack_level(value)
38
+ wid = self.unpack_world_delta(value)
39
+ sids = (level - 1).times.map { |i| self.unpack_sub_delta(i + 2, value) }
40
+ return [wid] + sids
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,97 @@
1
+ # encoding: utf-8
2
+
3
+ module GeoDelta
4
+ module Projector
5
+ DEG2RAD = Math::PI / 180.0 # 度をラジアンに変換するための係数
6
+ RAD2DEG = 180.0 / Math::PI # ラジアンを度に変換するための係数
7
+ DELTA_HEIGHT = Math.sqrt(0.75) # 一辺を1.0とする正三角形の高さ
8
+
9
+ # 緯度をメルカトルY座標に変換する
10
+ # -90.0 <= lat <= +90.0
11
+ # -1.0 <= my <= +1.0
12
+ def self.lat_to_my(lat)
13
+ return Math.atanh(Math.sin(lat * DEG2RAD)) / Math::PI
14
+ end
15
+
16
+ # 経度をメルカトルX座標に変換する
17
+ # -180.0 <= lng <= +180.0
18
+ # -1.0 <= mx <= +1.0
19
+ def self.lng_to_mx(lng)
20
+ return lng / 180.0
21
+ end
22
+
23
+ # メルカトルY座標を緯度に変換する
24
+ # -1.0 <= my <= +1.0
25
+ # -90.0 <= lat <= +90.0
26
+ def self.my_to_lat(my)
27
+ return Math.asin(Math.tanh(my * Math::PI)) * RAD2DEG
28
+ end
29
+
30
+ # メルカトルX座標を経度に変換する
31
+ # -1.0 <= mx <= +1.0
32
+ # -180.0 <= lng <= +180.0
33
+ def self.mx_to_lng(mx)
34
+ mx = (mx % 2.0) - 2.0
35
+ mx += 2.0 if mx < -1.0
36
+ return mx * 180.0
37
+ end
38
+
39
+ # メルカトルY座標から正規化Y座標に変換する
40
+ # -1.0 <= my <= +1.0
41
+ # -12.0 <= ny <= +12.0
42
+ def self.my_to_ny(my)
43
+ return my / DELTA_HEIGHT * 12.0
44
+ end
45
+
46
+ # メルカトルX座標から正規化X座標に変換する
47
+ # -1.0 <= my <= +1.0
48
+ # -12.0 <= ny <= +12.0
49
+ def self.mx_to_nx(mx)
50
+ return mx * 12.0
51
+ end
52
+
53
+ # 正規化Y座標からメルカトルY座標に変換する
54
+ # -12.0 <= ny <= +12.0
55
+ # -1.0 <= my <= +1.0
56
+ def self.ny_to_my(my)
57
+ return my / 12.0 * DELTA_HEIGHT
58
+ end
59
+
60
+ # 正規化X座標からメルカトルX座標に変換する
61
+ # -12.0 <= ny <= +12.0
62
+ # -1.0 <= my <= +1.0
63
+ def self.nx_to_mx(ny)
64
+ return ny / 12.0
65
+ end
66
+
67
+ def self.lat_to_ny(lat)
68
+ return self.my_to_ny(self.lat_to_my(lat))
69
+ end
70
+
71
+ def self.lng_to_nx(lng)
72
+ return self.mx_to_nx(self.lng_to_mx(lng))
73
+ end
74
+
75
+ def self.ny_to_lat(ny)
76
+ return self.my_to_lat(self.ny_to_my(ny))
77
+ end
78
+
79
+ def self.nx_to_lng(nx)
80
+ return self.mx_to_lng(self.nx_to_mx(nx))
81
+ end
82
+
83
+ def self.latlng_to_nxy(lat, lng)
84
+ return [
85
+ self.lng_to_nx(lng),
86
+ self.lat_to_ny(lat),
87
+ ]
88
+ end
89
+
90
+ def self.nxy_to_latlng(nx, ny)
91
+ return [
92
+ self.ny_to_lat(ny),
93
+ self.nx_to_lng(nx),
94
+ ]
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative "./delta_geometry"
4
+
5
+ module GeoDelta
6
+ module Region
7
+ def self.get_delta_ids_in_region(x1, y1, x2, y2, level)
8
+ ids = []
9
+
10
+ if x1 > x2
11
+ ids += self.get_delta_ids_in_region(x1, y1, +12.0, y2, level)
12
+ ids += self.get_delta_ids_in_region(x2, y1, +0.0, y2, level)
13
+ else
14
+ unit = 12.0 / (2 ** (level - 1))
15
+ u1 = unit
16
+ u2 = unit / 2
17
+
18
+ nw = GeoDelta::DeltaGeometry.get_delta_ids(x1, y1, level)
19
+ ne = GeoDelta::DeltaGeometry.get_delta_ids(x2, y1, level)
20
+ sw = GeoDelta::DeltaGeometry.get_delta_ids(x1, y2, level)
21
+ se = GeoDelta::DeltaGeometry.get_delta_ids(x2, y2, level)
22
+
23
+ proc {
24
+ sx, y = GeoDelta::DeltaGeometry.get_center(nw)
25
+ ex, _ = GeoDelta::DeltaGeometry.get_center(ne)
26
+
27
+ sx -= 24.0 if sx > ex
28
+
29
+ sxi = (sx / u2).floor
30
+ exi = (ex / u2).ceil
31
+ sxi -= 1 if !GeoDelta::DeltaGeometry.upper_delta?(nw) && x1 < sx && nw != sw
32
+ exi += 1 if !GeoDelta::DeltaGeometry.upper_delta?(ne) && x2 > ex && ne != se
33
+
34
+ (sxi..exi).each { |xi|
35
+ xx = xi * u2
36
+ ids << GeoDelta::DeltaGeometry.get_delta_ids(xx, y, level)
37
+ }
38
+ }.call
39
+
40
+ proc {
41
+ sx, y = GeoDelta::DeltaGeometry.get_center(sw)
42
+ ex, _ = GeoDelta::DeltaGeometry.get_center(se)
43
+
44
+ sx -= 24.0 if sx > ex
45
+
46
+ sxi = (sx / u2).floor
47
+ exi = (ex / u2).ceil
48
+ sxi -= 1 if GeoDelta::DeltaGeometry.upper_delta?(sw) && x1 < sx && nw != sw
49
+ exi += 1 if GeoDelta::DeltaGeometry.upper_delta?(se) && x2 > ex && ne != se
50
+
51
+ (sxi..exi).each { |xi|
52
+ xx = xi * u2
53
+ ids << GeoDelta::DeltaGeometry.get_delta_ids(xx, y, level)
54
+ }
55
+ }.call
56
+
57
+ proc {
58
+ syi = (y1 / u1).floor
59
+ eyi = (y2 / u1).ceil + 1
60
+ sxi = (x1 / u2).floor
61
+ exi = (x2 / u2).ceil
62
+ (eyi..syi).each { |yi|
63
+ yy = yi * unit - u2
64
+ (sxi..exi).each { |xi|
65
+ xx = xi * u2
66
+ ids << GeoDelta::DeltaGeometry.get_delta_ids(xx, yy, level)
67
+ }
68
+ }
69
+ }.call
70
+ end
71
+
72
+ ids.sort!
73
+ ids.uniq!
74
+
75
+ return ids
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,4 @@
1
+
2
+ module GeoDelta
3
+ VERSION = "0.0.2"
4
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: geodelta
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Yuya Kato
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-07-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
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: codeclimate-test-reporter
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: An implementation of GeoDelta for Ruby.
70
+ email:
71
+ - yuyakato@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - bin/console
82
+ - bin/setup
83
+ - circle.yml
84
+ - geodelta.gemspec
85
+ - lib/geodelta.rb
86
+ - lib/geodelta/core.rb
87
+ - lib/geodelta/delta_geometry.rb
88
+ - lib/geodelta/encoder.rb
89
+ - lib/geodelta/hex_geometry.rb
90
+ - lib/geodelta/hex_region.rb
91
+ - lib/geodelta/id_util.rb
92
+ - lib/geodelta/packer32.rb
93
+ - lib/geodelta/packer64.rb
94
+ - lib/geodelta/projector.rb
95
+ - lib/geodelta/region.rb
96
+ - lib/geodelta/version.rb
97
+ homepage: https://github.com/nayutaya/geodelta-ruby
98
+ licenses:
99
+ - MIT
100
+ metadata: {}
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 2.4.5
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: An implementation of GeoDelta for Ruby.
121
+ test_files: []