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

Traversing Neighbors Horizontally & Vertically (not an issue) #43

Open
TurtIeSocks opened this issue Aug 1, 2024 · 0 comments
Open

Comments

@TurtIeSocks
Copy link

First of all, thanks for the Rust port of the S2 lib! I still have a lot to learn about S2 in general but I'm running into a wall on this and feel like there must be a better way.

The Goal

Traverse multiple cells in any (edge) direction from a given cell.

Quick Example

  1. My API ingests some Polygon shape.
  2. I create a RegionCoverer to collect all cells in the bbox of that shape.
  3. Then I want to traverse those cells and collect the Lat/Lng point every 9 cells in a grid pattern.
Screenshot 2024-08-01 at 10 02 31 AM

Current Implementation

My initial implementation of this essentially called .edge_neighbors over and over n times to get to where I wanted to go. Since looking at source, I think I've moved onto something a little more sophisticated:

Structs:

#[derive(Debug, Clone, Copy)]
pub enum Dir {
    Up,
    Right,
    Down,
    Left,
}

#[derive(Debug, Clone, Copy)]
pub struct TraversalCell {
    pub cell: CellID,
    pub dir: Dir,
    pub prev_face: u8,
}

Traversal function:

// implementation for `TraversalCell`
    fn traverse(&mut self, count: u64) {
        let face = self.cell.face();
        let level = self.cell.level();
        let size = (cellid::size_ij(level) * count) as i32;
        let (f, cur_i, cur_j, _) = self.cell.face_ij_orientation();

        let (next_i, next_j) = match self.dir {
            Dir::Up => (cur_i, cur_j + size),
            Dir::Right => (cur_i + size, cur_j),
            Dir::Down => (cur_i, cur_j - size),
            Dir::Left => (cur_i - size, cur_j),
        };

        // this function was copied from source since it was originally private
        self.cell = from_face_ij_wrap(f, next_i, next_j).parent(level); 

        if self.cell.face() != self.prev_face {
            self.prev_face = face;
        }
    }

This works...okay. When the face stays the same, it's fine. When the face changes after a traversal, that's when things start to fall apart and where I'm running into a wall here.

The Dir enum is somewhat arbitrary, it was originally N, E, S, W, but since S2 cell neighbors don't always necessarily align with that I've swapped to something more generic.

I've looked at all sorts of various ways of manipulating the direction depending on the current face / current dir / previous face and for the most part have found some solutions that probably lean more in the hacky direction for solving most scenarios when there are 2 faces, but I haven't found something that will account for places where there are 3 faces. The issue that I'm running into with this is that some solutions will work for most scenarios but sometimes they have to be adapted entirely for other scenarios that then no longer work for the previously working ones.

Questions

  • Is there a better way that I should be approaching this problem entirely?
  • Can you clarify what you meant in the edge_neighbors docs here: Edges 0, 1, 2, 3 are in the down, right, up, left directions? Are down, right, up, left somewhat arbitrary or determined in some way that I don't understand...
  • And maybe an unnecessary question depending on the answer to the first one, but is there a more reliable way to be setting the dir property depending on the face?

Apologies, I know that's a lot of info but any help is appreciated, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant