Armax / Pokemon-GO-node-api

Pokemon GO api node.js library
MIT License
875 stars 198 forks source link

Most efficient method of "walking" an area #70

Closed brettstack closed 7 years ago

brettstack commented 7 years ago

So my understanding is that the heartbeat will scan 10 neighbor cells, but the only cell for which you get actual locations of pokemon is your origin cell. How is everyone "walking" to these neighboring cells and performing requests in order to retrieve the WildPokemon response for that cell?

A simple approach would be to just increment the lat/lng but this seems too basic. Can we somehow get the lat/long based off the cell id? Is there a better approach?

d-pollard commented 7 years ago

Am wondering too, a lot of the python "bots" are simply adding to the lat/long, but would like something more "user friendly" so to speak...

Example of how I am doing it...

    self.changePosition = function () {
        self.playerInfo.longitude = self.playerInfo.longitude + 0.000055;
        self.playerInfo.latitude = self.playerInfo.latitude + 0.000055;
        return true;
    };
brettstack commented 7 years ago

Yeah exactly. I would also add boundaries so it would scan a given area, but surely there are more efficient methods.

dotomaz commented 7 years ago

How about using the center of CellId? And then just calling next and prev on cell?

d-pollard commented 7 years ago

@dotomaz - how would you get the center of the cells long/lat?

dotomaz commented 7 years ago

Python library s2sphere https://github.com/sidewalklabs/s2sphere/blob/master/s2sphere/sphere.py has a Cell.get_center() method. Not sure what is the state of s2geometry node lib.

brettstack commented 7 years ago

Yeah I spent a while looking for a way to get lat/long from cell id, but there doesn't appear to be a great lib for node.

d-pollard commented 7 years ago

What about the current library used in the repo? I'll take a look and see what I can come up with once I push out the updates to the fort function/proto on my version.

brettstack commented 7 years ago

@d-pollard, any luck? I tried earlier using the the same lib but couldn't find a suitable method. Perhaps making a request to the library author?

On Thu, 21 Jul 2016, 22:49 d-pollard, notifications@github.com wrote:

What about the current library used in the repo? I'll take a look and see what I can come up with once I push out the updates to the fort function/proto on my version.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/Armax/Pokemon-GO-node-api/issues/70#issuecomment-234459986, or mute the thread https://github.com/notifications/unsubscribe-auth/ABy6lycw0Vh3qKWBmL_imgHxPdx_0R-Bks5qYFndgaJpZM4JSZs9 .

d-pollard commented 7 years ago

No luck, @billyriantono is the author I believe, maybe he can shed some insight on this for us.

dotomaz commented 7 years ago

We need Cell class. Witch is not ported yet.

billyriantono commented 7 years ago

@dotomaz & @breandr cell.get_center() already ported in latest version of s2geometry Thanks

brettstack commented 7 years ago

Thank you so much @billyriantono! Trying this out I'm getting a lot of "not a function" errors, eg. on latLng.toPoint(), cell.getCenter(), cellid.toPoint(), as well as other methods I have tried in order to get the center point of a cell... I have ran npm update and ensured I have the latest version in node_modules... Any ideas? Here's how I'm getting a S2CellId based on a heartbeat cell id:

const s2CellId = new s2.S2CellId(cell.S2CellId.toString())

billyriantono commented 7 years ago

@breandr here is the sample :

var origin = new s2.S2CellId(new s2.S2LatLng(44.0378862, 10.0458712));
var parent = origin.parent(15);
var child = parent.child(15);
console.log(parent.id());
console.log(child.id());
var cell = new s2.S2Cell(parent);
var center = cell.getCenter();
console.dir(center.z());
brettstack commented 7 years ago

Thanks @billyriantono. That example works, but what we need is the lat/lng, so I do:

var latLng = new s2.S2LatLng(center)
console.log(latLng.lat())
//TypeError: latLng.lat is not a function

I assume this part hasn't been ported yet? Even simplifying to its most basic form yields the same error:

var latLng = new s2.S2LatLng(44.0378862, 10.0458712)
console.log(latLng.lat())
//TypeError: latLng.lat is not a function
brettstack commented 7 years ago

Did anyone get this working?

dotomaz commented 7 years ago

This is how I currently get the position of the cells that are returned from the Heartbeat:

var cellId = new s2.S2CellId(hb.cells[i].S2CellId.toString()) ; var cell = new s2.S2Cell( cellId); var latLng = new s2.S2LatLng(cell.getCenter()).toString() ;

S2LatLng currently doesn't seem to have lat() and lng() methods. There are .lat and .lng properties. But they don't have the correct values. .toString method returns the location that can be easily parsed.

billyriantono commented 7 years ago

i will fix for lat and lng ... thanks for notify me .. :smiley:

coolaj86 commented 7 years ago

I'm working on putting this in the (web) client - which is where it is in the official app anyway and where it does need to be to build an app that truly functions indistinguishably from the regular app to not be blocked as a bot (i.e. it doesn't stay logged in 24 hours a day or run when you're not using it).

From what I've read it doesn't sound like it would be easy to get s2 running in the browser (which is where the heartbeat ought to happen, but here's the python code from PokemonGo-Map:

#Constants for Hex Grid
#Gap between vertical and horzonal "rows"
lat_gap_meters = 150
lng_gap_meters = 86.6

#111111m is approx 1 degree Lat, which is close enough for this
meters_per_degree = 111111
lat_gap_degrees = float(lat_gap_meters) / meters_per_degree

def calculate_lng_degrees(lat):
    return float(lng_gap_meters) / (meters_per_degree * math.cos(math.radians(lat)))

def generate_location_steps(initial_location, num_steps):

    ring = 1 #Which ring are we on, 0 = center
    lat_location = initial_location[0]
    lng_location = initial_location[1]

    yield (initial_location[0],initial_location[1], 0) #Middle circle

    while ring < num_steps:
        #Move the location diagonally to top left spot, then start the circle which will end up back here for the next ring
        #Move Lat north first
        lat_location += lat_gap_degrees
        lng_location -= calculate_lng_degrees(lat_location)

        for direction in range(6):
            for i in range(ring):
                if direction == 0: #Right
                    lng_location += calculate_lng_degrees(lat_location) * 2

                if direction == 1: #Right Down
                    lat_location -= lat_gap_degrees
                    lng_location += calculate_lng_degrees(lat_location)

                if direction == 2: #Left Down
                    lat_location -= lat_gap_degrees
                    lng_location -= calculate_lng_degrees(lat_location)

                if direction == 3: #Left
                    lng_location -= calculate_lng_degrees(lat_location) * 2

                if direction == 4: #Left Up
                    lat_location += lat_gap_degrees
                    lng_location -= calculate_lng_degrees(lat_location)

                if direction == 5: #Right Up
                    lat_location += lat_gap_degrees
                    lng_location += calculate_lng_degrees(lat_location)

                yield (lat_location, lng_location, 0) #Middle circle

        ring += 1

I'm currently translating this to JS and I'll post back when I'm done.

coolaj86 commented 7 years ago

Actually, I came across an s2 library and fixed a few bugs so that it works, and published it to npm and bower:

cokeeffekt commented 7 years ago

@coolaj86 so this will replace the dependency on s2geometry-node? if so that will be a god send...

coolaj86 commented 7 years ago

@cokeeffekt I'm almost there... I have code for a hilbert curve and I have code for converting lat/lng to an s2cell... just need to figure out how to stitch the two together (and then this will work on Windows without a compiler!!!)

cokeeffekt commented 7 years ago

plus will kill our largest and most problematic dependency :), i spent a bit of time tracking down a better solution to this the other day i did come across the package you found, but it got overwhelming how much time i was spending on it. Glad you have the patience to proceed through 👍

coolaj86 commented 7 years ago

Take a look here and see if this makes sense to you:

https://github.com/jonatkins/s2-geometry-javascript/issues/8

I'm reading up more on how the path stuff works, but maybe your eyes will see something obvious that mine are missing. Otherwise I may just have to look at it fresh in the morning...

brettstack commented 7 years ago

That would be so nice to not have a native dependency... Any ETA on when we might expect to move to the node implementation?

On Mon, 25 Jul 2016 at 22:36 AJ ONeal notifications@github.com wrote:

Take a look here and see if this makes sense to you:

jonatkins/s2-geometry-javascript#8 https://github.com/jonatkins/s2-geometry-javascript/issues/8

I'm reading up more on how the path stuff works, but maybe your eyes will see something obvious that mine are missing. Otherwise I may just have to look at it fresh in the morning...

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Armax/Pokemon-GO-node-api/issues/70#issuecomment-235167680, or mute the thread https://github.com/notifications/unsubscribe-auth/ABy6l_mmSmS-hzTBkJo_fxjrnM8AqZp7ks5qZZz3gaJpZM4JSZs9 .

cokeeffekt commented 7 years ago

@coolaj86 made some good progress, but i fear he only made it a few steps past where i got to before giving up, we are seeking help so hopefully we hear something back...

no ETA at this point i would assume. I'm going to see if i can play catch up and wrap my head around it later today. 99% of s2 libs on the internet are poorly documented and are basic copy pastes of each other because nobody really knows whats going on :| (well that is how it feels)

dotomaz commented 7 years ago

I was able to make a succesfull Hartbeat using simple-s2-node. The lib is not complete, but it has what poke.io needs to calculate cells. Check out @hunterjm fork of Pokemon-GO-node-api

coolaj86 commented 7 years ago

Well, I naively thought that there was a straight conversion between S2CellId and the hilbert curve quadtree - they're both 15 bits at level 15, they look so cute together in a console.log, etc...

but I can't find any example of converting between the two. It looks like I have to convert to an i,j S2Point first and then convert to one or the other...

I'm going to try to get this done today after I take a mid-morning nap. We'll see.

hunterjm commented 7 years ago

@dotmaz - the lib is definitely not complete, but I did hack enough stuff in there to calculate s2Cells correctly at level 15 for the API. It's off by one bit at other levels, probably due to the bignum library being used. Verified working for this use case, but haven't tested for others. I use it in my electron app so I can compile it for Windows.

For those interested in simulating "walking", I put this together using the Google Maps directions API last night (disclaimer: The code is not very clean/scalable... it was time for bed). It calculates an actual path that a real human would walk which should help when Niantic gets ban happy.

coolaj86 commented 7 years ago

Hahahaha!!!

After spending waaaaaaaaay too much time looking over various libraries and digging into code that had nothing to do with what I needed I finally figured out the SUPER SIMPLE missing piece from my fork of s2-geometry-javascript:

Magic Code:

'use strict';

var Long = require('long');
var S2 = {};

S2.FACE_BITS = 3;
S2.MAX_LEVEL = 30;
S2.POS_BITS = (2 * S2.MAX_LEVEL) + 1;

S2.fromFacePosLevel = function (faceN, posS, levelN) {
  if (!levelN) {
    levelN = posS.length;
  }
  if (posS.length > levelN) {
    posS = posS.substr(0, levelN);
  }
  while (posS.length < levelN) {
    posS = '0' + posS;
  }

  var posB = Long.fromString(posS, true, 4).toString(2);
  while (posB.length < (2 * levelN)) {
    posB = '0' + posB;
  }
  var bin = Long.fromString(faceN.toString(10), true, 10).toString(2);
  bin += posB;
  bin += '1';
  while (bin.length < (S2.FACE_BITS + S2.POS_BITS)) {
    bin += '0';
  }

  return Long.fromString(bin, true, 2);
};

Test:

console.log('');
console.log('Reference uint64 id:', '9749618446378729472')
console.log('Reference quadkey:', '4/032212303102210')
console.log('Reference binary:', Long.fromString('9749618446378729472', true, 10).toString(2));

var id = S2.fromFacePosLevel(4, '032212303102210');
console.log('');
console.log('Convert from quadkey to id:');
console.log(id.toString(10));
console.log(id.toString(2));

if ('9749618446378729472' !== id.toString(10)) {
  throw new Error("Didn't pass!!");
}
else {
  console.log("Everything is A-OK!");
}

I have to do the inverse and do some checking on the lat/lng conversion - the code I forked seems to lose precision, so I want to check against some other algorithms in the golang geo library, but it looks like there are just a few standard algos and this shouldn't be a problem.

Happy!!!

:D

:D

:D

coolaj86 commented 7 years ago

Fixed in https://github.com/Armax/Pokemon-GO-node-api/pull/165 by using pure JavaScript - no C++ or compiler necessary.

s2-geometry (the pure javascript port) can be used in the browser as well (although it does require long.js).