Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add S2 Geometry classes to Poke-API #28

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Changelog
------------------

0.1.0 (06-08-2016)
------------------
* **Feature** - Classes have been added to generate S2 Cell Ids in native Ruby (no external libraries), this covers the base S2 features and may not include advanced methods yet.

0.0.8 (03-08-2016)
------------------
* **Fix** - Ensure the endpoint is constantly updated if a new one is provided
Expand Down
44 changes: 37 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ Poke API is a port for Ruby from [pgoapi](https://github.com/tejado/pgoapi) and
* Use a throwaway account if possible.

## Supports
* S2 Geometry cells!
* PTC & Google Authentication supported (use full e-mail address for Google)
* Parses geolocation using Geocoder (parses addresses, postcodes, ip addresses, lat/long, etc)
* Ability to chain requests and receive response in a single call
* Logger available, you can also specify your own log formatter and/or log level
* Protobuf files are modified slightly to the new Proto syntax 3 (Ruby is incompatible with 2)
* A lot of RPC calls, they are listed under [`lib/poke-api/POGOProtos/Networking/Requests/RequestType.rb`](lib/poke-api/POGOProtos/Networking/Requests/RequestType.rb) (requires testing, but appears to be in working order)
* Lots of RPC calls, they are listed under [`lib/poke-api/POGOProtos/Networking/Requests/RequestType.rb`](lib/poke-api/POGOProtos/Networking/Requests/RequestType.rb)

## Installation
You can use bundler and refer directly to this repository
```
gem 'poke-go-api',
git: "https://github.com/nabeelamjad/poke-api.git",
tag: '0.0.8'
tag: '0.1.0'
```

Or, alternatively you can download the repository and run ``gem build poke-api.gemspec`` followed with ``gem install poke-api-0.0.8.gem``
Or, alternatively you can download the repository and run ``gem build poke-api.gemspec`` followed with ``gem install poke-api-0.1.0.gem``

The gem is also available by using ``gem install poke-go-api`` (poke-api was taken as a name already).

Expand Down Expand Up @@ -161,10 +161,40 @@ client.store_location('London')
#=> My custom logger - [+] Lat/Long: 51.5073509, -0.1277583
```

# Caveats & Workarounds
Google's S2 Geometry library has not been ported over to Ruby yet, you will need to find a way to obtain ``cell_ids`` to scan for (either through some library or through your own custom function). It is possible to do this to some degree using Geocoder, however it is not as extensive as S2-Geometry. More information can be found in [this article](http://blog.christianperone.com/2015/08/googles-s2-geometry-on-the-sphere-cells-and-hilbert-curve/). I welcome any pull request/suggestion on how to tackle this so I can add a ``Poke::API::Helper`` method to generate ``cell_ids``
# Generating S2 Cell Ids
You can use this helper method to generate cells, please note that not all S2 Geometry calls are supported as this has been ported over to native Ruby (without binding). Available instance methods on a **``S2CellId``** are ``parent``, ``level``, ``next`` and ``prev``.

A workaround can be found on [this comment](https://github.com/nabeelamjad/poke-api/issues/2#issuecomment-234742928) on how to obtain cell_ids for a given location and optionally enter a radius (defaults to 10 to your given location).
#### Example Usage
```ruby
require 'poke-api'

def get_cells(lat, lng, radius = 10)
s2_point = Poke::API::Geometry::S2LatLon.new(lat, lng).to_point
s2_cell = Poke::API::Geometry::S2CellId.from_point(s2_point).parent(15)

next_cell = s2_cell.next
prev_cell = s2_cell.prev

radius.times.reduce([s2_cell.id]) do |acc, el|
acc += [next_cell.id, prev_cell.id]
next_cell = next_cell.next
prev_cell = prev_cell.prev
acc
end.sort
end

client = Poke::API::Client.new
client.store_location('New York')

get_cells(client.lat, client.lng)
=> [9926595610352287744, 9926595612499771392, 9926595614647255040,
9926595616794738688, 9926595618942222336, 9926595621089705984,
9926595623237189632, 9926595625384673280, 9926595627532156928,
9926595629679640576, 9926595631827124224, 9926595633974607872,
9926595636122091520, 9926595638269575168, 9926595640417058816,
9926595642564542464, 9926595644712026112, 9926595646859509760,
9926595649006993408, 9926595651154477056, 9926595653301960704]
```

# Contribution
Any contributions are most welcome, I don't have much time to spend on this project so I appreciate everything.
Expand Down
38 changes: 30 additions & 8 deletions example.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,45 @@
require 'poke-api'
require 'pp'

# Instantiate the client
client = Poke::API::Client.new

# use Google auth with 'username@gmail.com', 'password', 'google'
client.login('username', 'password', 'ptc')
# Use Google auth with 'username@gmail.com', 'password', 'google'
client.store_location('New York')
client.login('username', 'password', 'ptc')

# Helper method to get cell ids, pass in your
# coordinates and an optional radius (default 10)
def get_cells(lat, lng, radius = 10)
s2_point = Poke::API::Geometry::S2LatLon.new(lat, lng).to_point
s2_cell = Poke::API::Geometry::S2CellId.from_point(s2_point).parent(15)

next_cell = s2_cell.next
prev_cell = s2_cell.prev

radius.times.reduce([s2_cell.id]) do |acc, _|
acc += [next_cell.id, prev_cell.id]
next_cell = next_cell.next
prev_cell = prev_cell.prev
acc
end.sort
end

# Get cells
cell_ids = get_cells(client.lat, client.lng)

# Please see [this comment](https://github.com/nabeelamjad/poke-api/issues/2#issuecomment-234742928)
# on how to obtain cell_ids for a given location and optionally
# enter a radius (defaults to 10 to your given location).
# Construct map objects call
client.get_map_objects(
latitude: client.lat,
longitude: client.lng,
since_timestamp_ms: [0],
cell_id: [9926595610352287744]
since_timestamp_ms: [0] * cell_ids.length,
cell_id: cell_ids
)

# Add more calls
client.get_player
client.get_inventory

pp client.call
# Call and view response
resp = client.call
pp resp
8 changes: 7 additions & 1 deletion lib/poke-api.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Load Poke-API related classes
# Load main Poke::API related classes
require 'poke-api/logging'
require 'poke-api/client'
require 'poke-api/helpers'
Expand All @@ -9,6 +9,12 @@
require 'poke-api/auth/ptc'
require 'poke-api/auth/google'

# Load Geometry libraries (native Ruby)
require 'poke-api/geometry/s2_base'
require 'poke-api/geometry/s2_cell_id'
require 'poke-api/geometry/s2_lat_lon'
require 'poke-api/geometry/s2_point'

# Load Google Generated POGOProtos
require 'poke-api/POGOProtos/Networking/Envelopes/RequestEnvelope'
require 'poke-api/POGOProtos/Networking/Envelopes/ResponseEnvelope'
10 changes: 5 additions & 5 deletions lib/poke-api/auth/google.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ class GOOGLE
include Logging
attr_reader :access_token, :provider

GOOGLE_LOGIN_ANDROID_ID = '9774d56d682e549c'
GOOGLE_LOGIN_ANDROID_ID = '9774d56d682e549c'.freeze
GOOGLE_LOGIN_SERVICE = 'audience:server:client_id:848232511240-7so421jotr' \
'2609rmqakceuu1luuq0ptb.apps.googleusercontent.com'
GOOGLE_LOGIN_APP = 'com.nianticlabs.pokemongo'
GOOGLE_LOGIN_CLIENT_SIG = '321187995bc7cdc2b5fc91b11a96e2baa8602c62'
'2609rmqakceuu1luuq0ptb.apps.googleusercontent.com'.freeze
GOOGLE_LOGIN_APP = 'com.nianticlabs.pokemongo'.freeze
GOOGLE_LOGIN_CLIENT_SIG = '321187995bc7cdc2b5fc91b11a96e2baa8602c62'.freeze

def initialize(username, password)
@username = username
Expand Down Expand Up @@ -40,7 +40,7 @@ def perform_request(method)
response = yield

if response['Error'] == 'NeedsBrowser'
raise Errors::GoogleTwoFactorAuthenticationFailure.new(response)
raise Errors::GoogleTwoFactorAuthenticationFailure
else
unless response['Token'] || response['Auth']
raise Errors::GoogleAuthenticationFailure.new(method, response)
Expand Down
6 changes: 3 additions & 3 deletions lib/poke-api/auth/ptc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ class PTC
attr_reader :access_token, :provider

PTC_LOGIN_URL = 'https://sso.pokemon.com/sso/login?service=https%3A%2F%2Fsso.pokemon.com' \
'%2Fsso%2Foauth2.0%2FcallbackAuthorize'
PTC_LOGIN_OAUTH = 'https://sso.pokemon.com/sso/oauth2.0/accessToken'
PTC_LOGIN_CLIENT_SECRET = 'w8ScCUXJQc6kXKw8FiOhd8Fixzht18Dq3PEVkUCP5ZPxtgyWsbTvWHFLm2wNY0JR'
'%2Fsso%2Foauth2.0%2FcallbackAuthorize'.freeze
PTC_LOGIN_OAUTH = 'https://sso.pokemon.com/sso/oauth2.0/accessToken'.freeze
PTC_LOGIN_CLIENT_SECRET = 'w8ScCUXJQc6kXKw8FiOhd8Fixzht18Dq3PEVkUCP5ZPxtgyWsbTvWHFLm2wNY0JR'.freeze

def initialize(username, password)
@username = username
Expand Down
3 changes: 1 addition & 2 deletions lib/poke-api/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ def store_location(loc)

def store_lat_lng(lat, lng)
logger.info "[+] Lat/Long: #{lat}, #{lng}"
@lat = lat
@lng = lng
@lat, @lng = lat, lng
end

def inspect
Expand Down
8 changes: 7 additions & 1 deletion lib/poke-api/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def initialize(token, response)
end

class GoogleTwoFactorAuthenticationFailure < StandardError
def initialize(response)
def initialize
super("Two-factor authentication not supported. Create an app-specific password to log in.")
end
end
Expand All @@ -48,6 +48,12 @@ def initialize
super("Unable to fetch endpoint, please try to login again.")
end
end

class InvalidLevel < StandardError
def initialize(level)
super("Level #{level} is invalid, must be between 0 and 30.")
end
end
end
end
end
50 changes: 50 additions & 0 deletions lib/poke-api/geometry/s2_base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
module Poke
module API
module Geometry
module S2Base
LINEAR_PROJECTION = 0
TAN_PROJECTION = 1
QUADRATIC_PROJECTION = 2

MAX_LEVEL = 30
NUM_FACES = 6
POS_BITS = 2 * MAX_LEVEL + 1
MAX_SIZE = 1 << MAX_LEVEL
SWAP_MASK = 0x01
INVERT_MASK = 0x02
LOOKUP_BITS = 4
POS_TO_OR = [SWAP_MASK, 0, 0, INVERT_MASK | SWAP_MASK].freeze
POS_TO_IJ = [[0, 1, 3, 2],
[0, 2, 3, 1],
[3, 2, 0, 1],
[3, 1, 0, 2]].freeze

LOOKUP_POS = [nil] * (1 << (2 * LOOKUP_BITS + 2))
LOOKUP_IJ = [nil] * (1 << (2 * LOOKUP_BITS + 2))

def self.lookup_cells(level, i, j, orig_orientation, pos, orientation)
return lookup_bits(i, j, orig_orientation, pos, orientation) if level == LOOKUP_BITS

r = POS_TO_IJ[orientation]
4.times do |index|
lookup_cells(
level + 1, (i << 1) + (r[index] >> 1), (j << 1) + (r[index] & 1),
orig_orientation, (pos << 2) + index, orientation ^ POS_TO_OR[index]
)
end
end

def self.lookup_bits(i, j, orig_orientation, pos, orientation)
ij = (i << LOOKUP_BITS) + j
LOOKUP_POS[(ij << 2) + orig_orientation] = (pos << 2) + orientation
LOOKUP_IJ[(pos << 2) + orig_orientation] = (ij << 2) + orientation
end

lookup_cells(0, 0, 0, 0, 0, 0)
lookup_cells(0, 0, 0, SWAP_MASK, 0, SWAP_MASK)
lookup_cells(0, 0, 0, INVERT_MASK, 0, INVERT_MASK)
lookup_cells(0, 0, 0, SWAP_MASK | INVERT_MASK, 0, SWAP_MASK | INVERT_MASK)
end
end
end
end
Loading