Skip to content

Commit

Permalink
added hierarchy functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Filimoa committed Dec 5, 2024
1 parent 42de4c6 commit 11c73fe
Show file tree
Hide file tree
Showing 8 changed files with 622 additions and 20 deletions.
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,19 @@ Here's the updated table with an additional column, **Supported**, which indicat
| `cell_to_boundary_wkt` | Convert cell ID to cell boundary | 🛑 |
| `get_resolution` | Get resolution number of cell ID ||
| `get_base_cell_number` | Get base cell number of cell ID | 🕥|
| `str_to_int` | Convert VARCHAR cell ID to UBIGINT | 🚧 |
| `int_to_str` | Convert BIGINT or UBIGINT cell ID to VARCHAR | 🚧 |
| `is_valid_cell` | True if this is a valid cell ID | 🚧 |
| `is_res_class_iii` | True if the cell's resolution is class III | 🕥|
| `is_pentagon` | True if the cell is a pentagon | 🕥|
| `get_icosahedron_faces` | List of icosahedron face IDs the cell is on | 🕥|
| `cell_to_parent` | Get coarser cell for a cell | 🚧 |
| `cell_to_children` | Get finer cells for a cell | 🚧 |
| `cell_to_center_child` | Provides the center child (finer) cell contained by cell at resolution childRes. | 🕥|
| `cell_to_child_pos` | Provides the position of the child cell within an ordered list of all children of the cell's parent at the specified resolution parentRes. The order of the ordered list is the same as that returned by cellToChildren. This is the complement of childPosToCell. | 🕥|
| `child_pos_to_cell` | Provides the child cell at a given position within an ordered list of all children of parent at the specified resolution childRes. The order of the ordered list is the same as that returned by cellToChildren. This is the complement of cellToChildPos. | 🕥|
| `compact_cells` | Compacts a collection of H3 cells by recursively replacing children cells with their parents if all children are present. Input cells must all share the same resolution. | 🕥|
| `uncompact_cells` | Uncompacts the set compactedSet of indexes to the resolution res. h3Set must be at least of size uncompactCellsSize(compactedSet, numHexes, res). | 🕥|
| `str_to_int` | Convert VARCHAR cell ID to UBIGINT | |
| `int_to_str` | Convert BIGINT or UBIGINT cell ID to VARCHAR | |
| `is_valid_cell` | True if this is a valid cell ID | |
| `is_res_class_iii` | True if the cell's resolution is class III | |
| `is_pentagon` | True if the cell is a pentagon | |
| `get_icosahedron_faces` | List of icosahedron face IDs the cell is on | |
| `cell_to_parent` | Get coarser cell for a cell | |
| `cell_to_children` | Get finer cells for a cell | |
| `cell_to_center_child` | Provides the center child (finer) cell contained by cell at resolution childRes. | |
| `cell_to_child_pos` | Provides the position of the child cell within an ordered list of all children of the cell's parent at the specified resolution parentRes. The order of the ordered list is the same as that returned by cellToChildren. This is the complement of childPosToCell. | |
| `child_pos_to_cell` | Provides the child cell at a given position within an ordered list of all children of parent at the specified resolution childRes. The order of the ordered list is the same as that returned by cellToChildren. This is the complement of cellToChildPos. | |
| `compact_cells` | Compacts a collection of H3 cells by recursively replacing children cells with their parents if all children are present. Input cells must all share the same resolution. | |
| `uncompact_cells` | Uncompacts the set compactedSet of indexes to the resolution res. h3Set must be at least of size uncompactCellsSize(compactedSet, numHexes, res). | |
| `h3_grid_disk` | Find cells within a grid distance | 🚧 |
| `grid_distance` | Find cells within a grid distance, sorted by distance | 🕥|
| `h3_grid_disk_unsafe` | Find cells within a grid distance, with no pentagon distortion | 🕥|
Expand Down
139 changes: 139 additions & 0 deletions h3_polars/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,142 @@ def get_resolution(expr: IntoExprColumn) -> pl.Expr:
function_name="get_resolution",
is_elementwise=True,
)


def str_to_int(expr: IntoExprColumn) -> pl.Expr:
return register_plugin_function(
args=[expr],
plugin_path=LIB,
function_name="str_to_int",
is_elementwise=True,
)


def int_to_str(expr: IntoExprColumn) -> pl.Expr:
return register_plugin_function(
args=[expr],
plugin_path=LIB,
function_name="int_to_str",
is_elementwise=True,
)


def is_valid_cell(expr: IntoExprColumn) -> pl.Expr:
return register_plugin_function(
args=[expr],
plugin_path=LIB,
function_name="is_valid_cell",
is_elementwise=True,
)


def is_pentagon(expr: IntoExprColumn) -> pl.Expr:
return register_plugin_function(
args=[expr],
plugin_path=LIB,
function_name="is_pentagon",
is_elementwise=True,
)


def is_res_class_III(expr: IntoExprColumn) -> pl.Expr:
return register_plugin_function(
args=[expr],
plugin_path=LIB,
function_name="is_res_class_III",
is_elementwise=True,
)


def get_icosahedron_faces(expr: IntoExprColumn) -> pl.Expr:
return register_plugin_function(
args=[expr],
plugin_path=LIB,
function_name="get_icosahedron_faces",
is_elementwise=True,
)


def cell_to_parent(cell: IntoExprColumn, resolution: int | None = None) -> pl.Expr:
return register_plugin_function(
args=[cell],
plugin_path=LIB,
function_name="cell_to_parent",
is_elementwise=True,
kwargs={"resolution": resolution},
)


def cell_to_center_child(
cell: IntoExprColumn, resolution: int | None = None
) -> pl.Expr:
return register_plugin_function(
args=[cell],
plugin_path=LIB,
function_name="cell_to_center_child",
is_elementwise=True,
kwargs={"resolution": resolution},
)


def cell_to_children_size(
cell: IntoExprColumn, resolution: int | None = None
) -> pl.Expr:
return register_plugin_function(
args=[cell],
plugin_path=LIB,
function_name="cell_to_children_size",
is_elementwise=True,
kwargs={"resolution": resolution},
)


def cell_to_children(cell: IntoExprColumn, resolution: int) -> pl.Expr:
return register_plugin_function(
args=[cell],
plugin_path=LIB,
function_name="cell_to_children",
is_elementwise=True,
kwargs={"resolution": resolution},
)


def cell_to_child_pos(cell: IntoExprColumn, resolution: int = 0) -> pl.Expr:
return register_plugin_function(
args=[cell],
plugin_path=LIB,
function_name="cell_to_child_pos",
is_elementwise=True,
kwargs={"resolution": resolution},
)


def child_pos_to_cell(
parent: IntoExprColumn, pos: IntoExprColumn, resolution: int = 0
) -> pl.Expr:
return register_plugin_function(
args=[parent, pos],
plugin_path=LIB,
function_name="child_pos_to_cell",
is_elementwise=True,
kwargs={"resolution": resolution},
)


def compact_cells(cells: IntoExprColumn) -> pl.Expr:
return register_plugin_function(
args=[cells],
plugin_path=LIB,
function_name="compact_cells",
is_elementwise=True,
)


def uncompact_cells(cells: IntoExprColumn, resolution: int) -> pl.Expr:
return register_plugin_function(
args=[cells],
plugin_path=LIB,
function_name="uncompact_cells",
is_elementwise=True,
kwargs={"resolution": resolution},
)
164 changes: 164 additions & 0 deletions src/engine/hierarchy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
use super::utils::parse_cell_indices;
use h3o::{CellIndex, Resolution};
use polars::prelude::*;
use rayon::prelude::*;

fn get_target_resolution(cell: CellIndex, target_res: Option<u8>) -> Option<Resolution> {
match target_res {
Some(res) => Resolution::try_from(res).ok(),
None => {
let curr_res = cell.resolution();
// Get next resolution if None provided
curr_res.succ()
},
}
}

pub fn cell_to_parent(cell_series: &Series, parent_res: Option<u8>) -> PolarsResult<Series> {
let cells = parse_cell_indices(cell_series)?;

let parents: UInt64Chunked = cells
.into_par_iter()
.map(|cell| {
cell.and_then(|idx| {
let target_res = if let Some(res) = parent_res {
Resolution::try_from(res).ok()
} else {
idx.resolution().pred()
};
target_res.and_then(|res| idx.parent(res))
})
.map(Into::into)
})
.collect();

Ok(parents.into_series())
}

pub fn cell_to_center_child(cell_series: &Series, child_res: Option<u8>) -> PolarsResult<Series> {
let cells = parse_cell_indices(cell_series)?;

let center_children: UInt64Chunked = cells
.into_par_iter()
.map(|cell| {
cell.and_then(|idx| {
let target_res = get_target_resolution(idx, child_res)?;
idx.center_child(target_res)
})
.map(Into::into)
})
.collect();

Ok(center_children.into_series())
}

pub fn cell_to_children_size(cell_series: &Series, child_res: Option<u8>) -> PolarsResult<Series> {
let cells = parse_cell_indices(cell_series)?;

let sizes: UInt64Chunked = cells
.into_par_iter()
.map(|cell| {
cell.map(|idx| {
let target_res = get_target_resolution(idx, child_res)
.unwrap_or_else(|| idx.resolution().succ().unwrap_or(idx.resolution()));
idx.children_count(target_res)
})
})
.collect();

Ok(sizes.into_series())
}

pub fn cell_to_children(cell_series: &Series, child_res: Option<u8>) -> PolarsResult<Series> {
let cells = parse_cell_indices(cell_series)?;

let children: ListChunked = cells
.into_par_iter()
.map(|cell| {
cell.map(|idx| {
let target_res = get_target_resolution(idx, child_res)
.unwrap_or_else(|| idx.resolution().succ().unwrap_or(idx.resolution()));
let children: Vec<u64> = idx.children(target_res).map(Into::into).collect();
Series::new(PlSmallStr::from(""), children.as_slice())
})
})
.collect();

Ok(children.into_series())
}

pub fn cell_to_child_pos(child_series: &Series, parent_res: u8) -> PolarsResult<Series> {
let cells = parse_cell_indices(child_series)?;

let positions: UInt64Chunked = cells
.into_par_iter()
.map(|cell| {
cell.and_then(|idx| {
let parent_res = Resolution::try_from(parent_res).ok()?;
idx.child_position(parent_res)
})
})
.collect();

Ok(positions.into_series())
}
pub fn child_pos_to_cell(
parent_series: &Series,
child_res: u8,
pos_series: &Series,
) -> PolarsResult<Series> {
let parents = parse_cell_indices(parent_series)?;
let positions = pos_series.u64()?;

// Convert positions to Vec to ensure we can do parallel iteration
let pos_vec: Vec<Option<u64>> = positions.into_iter().collect();

let children: UInt64Chunked = parents
.into_par_iter()
.zip(pos_vec.into_par_iter())
.map(|(parent, pos)| match (parent, pos) {
(Some(parent), Some(pos)) => {
let child_res = Resolution::try_from(child_res).ok()?;
parent.child_at(pos, child_res).map(Into::into)
},
_ => None,
})
.collect();

Ok(children.into_series())
}

pub fn compact_cells(cell_series: &Series) -> PolarsResult<Series> {
let cells = parse_cell_indices(cell_series)?;

// Convert to Vec to pass to compact function
let cell_vec: Vec<_> = cells.into_iter().filter_map(|x| x).collect();

let compacted = CellIndex::compact(cell_vec)
.map_err(|e| PolarsError::ComputeError(format!("Compaction error: {}", e).into()))?;

let compacted_cells: UInt64Chunked = compacted.map(|idx| Some(u64::from(idx))).collect();

Ok(compacted_cells.into_series())
}

pub fn uncompact_cells(cell_series: &Series, res: u8) -> PolarsResult<Series> {
let cells = parse_cell_indices(cell_series)?;
let target_res = Resolution::try_from(res)
.map_err(|_| PolarsError::ComputeError("Invalid resolution".into()))?;

// Convert to Vec to pass to uncompact function
let cell_vec: Vec<_> = cells.into_iter().filter_map(|x| x).collect();

let uncompacted: ListChunked = vec![Some(Series::new(
PlSmallStr::from(""),
CellIndex::uncompact(cell_vec, target_res)
.map(u64::from)
.collect::<Vec<_>>()
.as_slice(),
))]
.into_iter()
.collect();

Ok(uncompacted.into_series())
}
Loading

0 comments on commit 11c73fe

Please sign in to comment.