Skip to content

Conversation

kylebarron
Copy link
Member

@kylebarron kylebarron commented Oct 10, 2025

cc @felixpalmer

image

Works in principle with latest deck.gl-layers release.

Change list

  • Adds H3HexagonLayer as a core layer type.
  • Implements h3 index validation in pure numpy, so that users can have data validated before it goes to JS (where it's hard to surface any data errors)
  • Implement str_to_h3 vectorized function that converts str input into a uint64 h3 array.
  • Implement H3Accessor traitlet that takes in either an array of str or int, validates them, and then packs array as uint64 type to send to the frontend.

todo

  • update layer docs
  • Update website docs for this layer
  • Implement layer bounds computation

Closes #302, for #885

@kylebarron kylebarron changed the title WIP: h3 layer feat: H3 layer Oct 13, 2025
@github-actions github-actions bot added the feat label Oct 13, 2025
@kylebarron
Copy link
Member Author

Benchmark of h3 string parsing:

import numpy as np
import pandas as pd
import pyarrow as pa

import h3.api.numpy_int as h3
from lonboard import H3HexagonLayer, Map
from lonboard._h3 import h3_to_str
from lonboard._h3._str_to_h3 import str_to_h3

VALID_INDICES = np.array(
    [
        0x8075FFFFFFFFFFF,
        0x81757FFFFFFFFFF,
        0x82754FFFFFFFFFF,
        0x83754EFFFFFFFFF,
        0x84754A9FFFFFFFF,
        0x85754E67FFFFFFF,
        0x86754E64FFFFFFF,
        0x87754E64DFFFFFF,
        0x88754E6499FFFFF,
        0x89754E64993FFFF,
        0x8A754E64992FFFF,
        0x8B754E649929FFF,
        0x8C754E649929DFF,
        0x8D754E64992D6FF,
        0x8E754E64992D6DF,
        0x8F754E64992D6D8,
    ],
    dtype=np.uint64,
)

hex_str = h3_to_str(VALID_INDICES)
large_hex_str = np.repeat(hex_str, 10000)

%timeit parsed_loop = np.array([int(h, 16) for h in large_hex_str])
# 20.3 ms ± 886 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit parsed_h3_api = np.array([h3.str_to_int(h) for h in large_hex_str])
# 26.9 ms ± 200 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit parsed = str_to_h3(large_hex_str)
# 7.25 ms ± 170 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

@kylebarron
Copy link
Member Author

Benchmark of h3 cell validation:

import numpy as np
import pandas as pd
import pyarrow as pa

import h3.api.numpy_int as h3
from lonboard import H3HexagonLayer, Map
from lonboard._h3 import h3_to_str, validate_h3_indices

VALID_INDICES = np.array(
    [
        0x8075FFFFFFFFFFF,
        0x81757FFFFFFFFFF,
        0x82754FFFFFFFFFF,
        0x83754EFFFFFFFFF,
        0x84754A9FFFFFFFF,
        0x85754E67FFFFFFF,
        0x86754E64FFFFFFF,
        0x87754E64DFFFFFF,
        0x88754E6499FFFFF,
        0x89754E64993FFFF,
        0x8A754E64992FFFF,
        0x8B754E649929FFF,
        0x8C754E649929DFF,
        0x8D754E64992D6FF,
        0x8E754E64992D6DF,
        0x8F754E64992D6D8,
    ],
    dtype=np.uint64,
)

large_hex = np.repeat(VALID_INDICES, 10000)
%timeit validate_h3_indices(large_hex)
# 3.68 ms ± 96.5 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit all([h3.is_valid_cell(h) for h in large_hex])
# 15 ms ± 157 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

@kylebarron
Copy link
Member Author

kylebarron commented Oct 13, 2025

Benchmark of bounds computation using h3:

import numpy as np
import pandas as pd
import pyarrow as pa

import h3.api.numpy_int as h3
from lonboard import H3HexagonLayer, Map
from lonboard._h3 import h3_to_str, validate_h3_indices

VALID_INDICES = np.array(
    [
        0x8075FFFFFFFFFFF,
        0x81757FFFFFFFFFF,
        0x82754FFFFFFFFFF,
        0x83754EFFFFFFFFF,
        0x84754A9FFFFFFFF,
        0x85754E67FFFFFFF,
        0x86754E64FFFFFFF,
        0x87754E64DFFFFFF,
        0x88754E6499FFFFF,
        0x89754E64993FFFF,
        0x8A754E64992FFFF,
        0x8B754E649929FFF,
        0x8C754E649929DFF,
        0x8D754E64992D6FF,
        0x8E754E64992D6DF,
        0x8F754E64992D6D8,
    ],
    dtype=np.uint64,
)

large_hex = np.repeat(VALID_INDICES, 10000)

def cell_bounds(h):
    boundary = np.array(h3.cell_to_boundary(h))  # lat/lon pairs
    min_lat = boundary[:, 0].min()
    max_lat = boundary[:, 0].max()
    min_lon = boundary[:, 1].min()
    max_lon = boundary[:, 1].max()
    return min_lat, max_lat, min_lon, max_lon

%%timeit
# Apply to all cells
bounds_array = np.array([cell_bounds(c) for c in large_hex])
min_lat = bounds_array[:, 0].min()
max_lat = bounds_array[:, 0].max()
min_lon = bounds_array[:, 1].min()
max_lon = bounds_array[:, 1].max()
856 ms ± 6.26 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Almost a second for 160,000 h3 cells 😬

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

H3 support

1 participant