H3-Pandas icon indicating copy to clipboard operation
H3-Pandas copied to clipboard

ENH: Support LineStrings

Open DahnJ opened this issue 4 years ago • 2 comments

A great feature of the hexagonal shape of H3 is how naturally it can delineate LineStrings such as roads, rivers in a connected and visually pleasing way.

It would thus make sense to provide a method that would provide a "linestring polyfill", generating a continuous string of H3 cells that cover the linestring. This could be done in two steps:

  • Index the coordinates of the linestring using geo_to_h3
  • Fill-in the gaps using h3_line

The interface and implementation could be similar to other similar methods, e.g. polyfill

DahnJ avatar Jun 29 '21 16:06 DahnJ

I was just working on an H3 demo for a GIS forum, and wanted to demonstrate this. Came up with:

from shapely.geometry.linestring import LineString
from typing import Iterator

def sequential_deduplication(func: Iterator[str]) -> Iterator[str]:
    '''
    Decorator that doesn't permit two consecutive items to be the same
    '''
    def inner(*args):
        iterable = func(*args)
        last = None
        while (cell := next(iterable, None)) is not None:
            if cell != last:
                yield cell
            last = cell
    return inner

@sequential_deduplication # prevent consecutive repetition, esp. at low res
def h3polyline(line: LineString, resolution: int) -> Iterator[str]:
    '''
    Iterator yielding H3 cells representing a line,
    retaining order and any self-intersections
    '''
    coords = zip(line.coords, line.coords[1:])
    while (vertex_pair := next(coords, None)) is not None:
        i, j = vertex_pair
        a = h3.geo_to_h3(*i[::-1], resolution)
        b = h3.geo_to_h3(*j[::-1], resolution)
        yield from h3.h3_line(a, b) # inclusive of a and b

Just thought it might help someone. I'm not sure there's any particular advantage doing this with generators, unless/until h3.h3_line is itself a generator.

Not sure what is supposed to happen with repeated cells, but intuitively I thought it made sense to attempt to retain order and self-intersections rather than to just return an unordered set.

Doesn't consider MultiLineStrings.

alpha-beta-soup avatar Apr 26 '22 04:04 alpha-beta-soup

Version 2, adding multilinestring as valid input type.

from shapely.geometry.linestring import LineString
from shapely.geometry.multilinestring import MultiLineString
from typing import Iterator, Union

def sequential_deduplication(func: Iterator[str]) -> Iterator[str]:
    '''
    Decorator that doesn't permit two consecutive items to be the same
    '''
    def inner(*args):
        iterable = func(*args)
        last = None
        while (cell := next(iterable, None)) is not None:
            if cell != last:
                yield cell
            last = cell
    return inner

@sequential_deduplication
def h3polyline(line: Union[LineString, MultiLineString], resolution: int) -> Iterator[str]:
    '''
    Iterator yeilding H3 cells representing a (multi)line,
    retaining order and self-intersections
    '''
    if line.geom_type == 'MultiLineString':
        # Recurse after getting component linestrings from the multiline
        for l in map(lambda geom: h3polyline(geom, resolution), line.geoms):
            yield from l
    else:
        coords = zip(line.coords, line.coords[1:])
        while (vertex_pair := next(coords, None)) is not None:
            i, j = vertex_pair
            a = h3.geo_to_h3(*i[::-1], resolution)
            b = h3.geo_to_h3(*j[::-1], resolution)
            yield from h3.h3_line(a, b) # inclusive of a and b

alpha-beta-soup avatar Apr 26 '22 22:04 alpha-beta-soup

Closed by https://github.com/DahnJ/H3-Pandas/pull/24 released as 0.2.6 🎉

DahnJ avatar Nov 21 '23 19:11 DahnJ