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

ENH: Support LineStrings #11

Closed
DahnJ opened this issue Jun 29, 2021 · 3 comments
Closed

ENH: Support LineStrings #11

DahnJ opened this issue Jun 29, 2021 · 3 comments
Labels
enhancement New feature or request

Comments

@DahnJ
Copy link
Owner

DahnJ commented Jun 29, 2021

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 DahnJ added the enhancement New feature or request label Jun 29, 2021
@alpha-beta-soup
Copy link
Contributor

alpha-beta-soup commented Apr 26, 2022

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
Copy link
Contributor

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

@DahnJ
Copy link
Owner Author

DahnJ commented Nov 21, 2023

Closed by #24 released as 0.2.6 🎉

@DahnJ DahnJ closed this as completed Nov 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants