Esempio n. 1
0
def vor_poly(in_fc, gdb, name, out_kind):
    """Return the Voronoi/Theissen poly* features."""
    tmp = MultipartToSinglepart(in_fc, r"memory\in_fc_temp")
    g, oids, shp_kind, k, m, SR = _in_(tmp, info="voronoi")
    out = []
    L, B, R, T = g.aoi_extent()  # full extent for infinity circle
    xc, yc = np.array([(R - L) / 2., (T - B) / 2.])
    radius = max((R - L), (T - B)) * 10
    inf_circ = circle(radius, xc=xc, yc=yc)
    pnts = [np.vstack((g.XY, inf_circ))]
    for ps in pnts:
        if len(ps) > 2:
            ps = np.unique(ps, axis=0)
        avg = np.mean(ps, axis=0)
        p = ps - avg
        tri = Voronoi(p)
        for region in tri.regions:
            if -1 not in region:
                polygon = np.array([tri.vertices[i] + avg for i in region])
                if len(polygon) >= 3:
                    out.append(polygon)
    x, y = g.LL
    out = arrays_to_Geo(out, kind=2, info="voronoi")
    g0 = out.translate(dx=x, dy=y)
    #
    tmp = temp_fc(g0, "tmp", shp_kind, SR)
    ext_poly = np.array([[L, B], [L, T], [R, T], [R, B], [L, B]])
    ext_poly = arrays_to_Geo([ext_poly], kind=2, info="extent")
    ext_poly = ext_poly.translate(dx=x, dy=y)
    tmp1 = temp_fc(ext_poly, "tmp1", shp_kind, SR)
    final = gdb.replace("\\", "/") + "/" + name
    Clip(tmp, tmp1, final)  # arcpy.analysis.Clip
    return
Esempio n. 2
0
def hex_pointy(dx=1,
               dy=1,
               x_cols=1,
               y_rows=1,
               orig_x=0,
               orig_y=0,
               kind=2,
               asGeo=True):
    """Create pointy hexagons. Also called ``traverse hexagons``.

    Parameters
    ----------
    See `rectangles` for shared parameter explanation.
    """
    p_rad = np.deg2rad([150., 90, 30., -30., -90., -150., 150.])
    X = np.cos(p_rad) * dx
    Y = np.sin(p_rad) * dy  # scaled hexagon about 0, 0
    seed = np.array(list(zip(X, Y)))
    dx = dx * np.sqrt(3.) / 2.0
    dy = dy * 1.5
    hexs = [seed + [dx * i * 2, 0] for i in range(0, x_cols)]
    m = len(hexs)
    for j in range(1, y_rows):  # create the other rows
        hexs += [hexs[h] + [dx * (j % 2), dy * j] for h in range(m)]
    if asGeo:
        frmt = "dx {}, dy {}, x_cols {}, y_rows {}, LB ({},{})"
        txt = frmt.format(dx, dy, x_cols, y_rows, orig_x, orig_y)
        return arrays_to_Geo(hexs, kind=2, info=txt)
    return hexs
Esempio n. 3
0
def ellipse(x_radius=1.0,
            y_radius=1.0,
            theta=10.,
            xc=0.0,
            yc=0.0,
            kind=2,
            asGeo=True):
    """Produce an ellipse depending on parameters.

    Parameters
    ----------
    radius : number
        Distance from centre in the X and Y directions.
    theta : number
        Angle of densification of the shape around 360 degrees.
    """
    angles = np.deg2rad(np.arange(180.0, -180.0 - theta, step=-theta))
    x_s = x_radius * np.cos(angles) + xc  # X values
    y_s = y_radius * np.sin(angles) + yc  # Y values
    # pnts = np.array(list(zip(x_s, y_s)))  # the slow way
    pnts = np.zeros((x_s.shape[0], 2), x_s.dtype)
    pnts[:, 0] = x_s
    pnts[:, 1] = y_s
    if asGeo:
        if not isinstance(pnts, list):
            pnts = [pnts]
        frmt = "x_rad {}, y_rad {}, theta {}, x_c {}, y_c {}"
        txt = frmt.format(x_radius, y_radius, theta, xc, yc)
        return arrays_to_Geo(pnts, kind=2, info=txt)
    return pnts
Esempio n. 4
0
def hex_flat(dx=1,
             dy=1,
             x_cols=1,
             y_rows=1,
             orig_x=0,
             orig_y=0,
             kind=2,
             asGeo=True):
    """Generate the points for the flat-headed hexagon.

    Parameters
    ----------
    See `rectangles` for shared parameter explanation.
    """
    f_rad = np.deg2rad([180., 120., 60., 0., -60., -120., -180.])
    X = np.cos(f_rad) * dy
    Y = np.sin(f_rad) * dy  # scaled hexagon about 0, 0
    seed = np.array(list(zip(X, Y)))  # array of coordinates
    dx = dx * 1.5
    dy = dy * np.sqrt(3.) / 2.0
    hexs = [seed + [dx * i, dy * (i % 2)] for i in range(0, x_cols)]
    m = len(hexs)
    for j in range(1, y_rows):  # create the other rows
        hexs += [hexs[h] + [0, dy * 2 * j] for h in range(m)]
    if asGeo:
        frmt = "dx {}, dy {}, x_cols {}, y_rows {}, LB ({},{})"
        txt = frmt.format(dx, dy, x_cols, y_rows, orig_x, orig_y)
        return arrays_to_Geo(hexs, kind=2, info=txt)
    return hexs
Esempio n. 5
0
def arc_sector(outer=10, inner=9, start=1, stop=6, step=0.1, asGeo=True):
    """Form an arc sector bounded by a distance specified by two radii.

    Parameters
    ----------
    outer : number
        outer radius of the arc sector
    inner : number
        inner radius
    start : number
        start angle of the arc
    stop : number
        end angle of the arc
    step : number
        the angle densification step

    Requires
    --------
    `arc_` is used to produce the arcs, the top arc is rotated clockwise and
    the bottom remains in the order produced to help form closed-polygons.
    """
    s_s = [start, stop]
    s_s.sort()
    start, stop = s_s
    top = arc_(outer, start, stop, step, 0.0, 0.0)
    top = top[::-1]
    bott = arc_(inner, start, stop, step, 0.0, 0.0)
    close = top[0]
    pnts = np.concatenate((top, bott, [close]), axis=0)
    if asGeo:
        return arrays_to_Geo(pnts, kind=2)
    return pnts
Esempio n. 6
0
def arc_(radius=100, start=0, stop=1, step=0.1, xc=0.0, yc=0.0, asGeo=True):
    """Create an arc from a specified radius, centre and start/stop angles.

    Parameters
    ----------
    radius : number
        cirle radius from which the arc is obtained
    start, stop, step : numbers
        angles in degrees
    xc, yc : number
        center coordinates in projected units
    as_list : boolean
        False, returns an array.  True yields a list

    Returns
    -------
      Points on the arc as an array

    >>> # arc from 0 to 90 in 5 degree increments with radius 2 at (0, 0)
    >>> a0 = arc_(radius=2, start=0, stop=90, step=5, xc=0.0, yc=0.0)
    """
    start, stop = sorted([start, stop])
    angle = np.deg2rad(np.arange(start, stop, step))
    x_s = radius * np.cos(angle)  # X values
    y_s = radius * np.sin(angle)  # Y values
    pnts = np.array([x_s, y_s]).T + [xc, yc]
    if asGeo:
        return arrays_to_Geo(pnts, kind=2)
    return pnts
def merge_(this, to_this):
    """
    Merge `this` geometry and `to_this` geometry.  The direction is important.

    Parameters
    ----------
    this : array(s) or a Geo array
        The geometry to merge to the existing geometry (`to_this`).
    to_this : Geo array
        The Geo array to receive the new geometry.

    Notes
    -----
    The `this` array can be a single array, a list of arrays or a Geo array.
    If you want to append object array(s) (dtype= 'O'), then convert to a
    list of arrays or a list of lists first.

    During the merge operation, overlapping geometries are not intersected.

    Returns
    -------
    A new Geo array.

    this = np.array([[0, 8.], [5., 13.], [5., 8.], [0., 8]])
    b = this + [5, 2]
    this = [a, b]
    to_this = s0
    """
    a = this      # --- rename to simplify the input names
    b = to_this   # merge a to b, or this to_this
    if not hasattr(b, 'IFT'):
        b = npGeo.arrays_to_Geo(b)
    b_XY = b.XY
    b_IFT = b.IFT
    if hasattr(this, 'IFT'):
        if a.K != b.K:
            print("\nGeo array `kind` is not the same.\n")
            return None
        a_XY = a.XY
        a_IFT = a.IFT
    else:
        a = np.asarray(a)
        if a.ndim == 2:
            a = [a]
        a_XY, a_IFT, extent = npGeo.array_IFT(a)
        a_XY = a_XY + extent[0]
    last = b.IFT[-1, :]
    add_ = []
    for i, row in enumerate(a_IFT, 1):
        add_.append([last[0] + i, last[2] + row[1],
                     last[2] + row[2]] + list(row[3:]))
    add_ = np.atleast_2d(add_)
    new_ift = np.vstack((b_IFT, add_))
    xys = np.vstack((b_XY, a_XY))
    kind = b.K
    sr = b.SR
    out = npGeo.Geo(xys, IFT=new_ift, Kind=kind, Extent=None, Info="", SR=sr)
    return out
Esempio n. 8
0
def rectangle(dx=1,
              dy=-1,
              x_cols=1,
              y_rows=1,
              orig_x=0,
              orig_y=1,
              kind=2,
              asGeo=True):
    """Create a point array to represent a series of rectangles or squares.

    Parameters
    ----------
    dx, dy : number
        x direction increment, +ve moves west to east, left/right.
        y direction increment, -ve moves north to south, top/bottom.
    x_cols, y_rows : integers
        The number of columns and rows to produce.
    orig_x, orig_y : number
        Planar coordinates assumed.  You can alter the location of the origin
        by specifying the correct combination of (dx, dy) and (orig_x, orig_y).
        The defaults produce a clockwise, closed-loop geometry, beginning and
        ending in the upper left.
    kind, asGeo :
        These relate to Geo arrays

    Example
    -------
    Stating the obvious... squares form when dx == dy.

    X = [0.0, 0.0, dx, dx, 0.0] # X, Y values for a unit square
    Y = [0.0, dy, dy, 0.0, 0.0]

    Cells are constructed clockwise from the bottom-left.  The rectangular grid
    is constructed from the top-left.  Specifying an origin (upper left) of
    (0, 2) yields a bottom-right corner of (3,0) when the following are used.

    >>> z = rectangle(dx=1, dy=1, x_cols=3, y_rows=2, orig_x=0, orig_y=2,
    ...               kind=2, asGeo=False)

    The first `cell` will be in the top-left and the last `cell` in the
    bottom-right.
    """
    seed = np.array([[0.0, 0.0], [0.0, dy], [dx, dy], [dx, 0.0], [0.0, 0.0]])
    a = [
        seed + [j * dx, i * dy]  # make the shapes
        for i in range(0, y_rows)  # cycle through the rows
        for j in range(0, x_cols)
    ]  # cycle through the columns
    a = np.asarray(a) + [orig_x, orig_y - dy]
    if asGeo:
        frmt = "dx {}, dy {}, x_cols {}, y_rows {}, LB ({},{})"
        txt = frmt.format(dx, dy, x_cols, y_rows, orig_x, orig_y)
        return arrays_to_Geo(a, kind=2, info=txt)
    return a
Esempio n. 9
0
def circle_ring(outer=100,
                inner=0,
                theta=10,
                rot=0,
                scale=1,
                xc=0.0,
                yc=0.0,
                asGeo=True):
    """Create a multi-ring buffer around a center point (xc, yc).

    Parameters
    ----------
    outer, inner : number
        Outer and inner radius in planar units
    theta : number
        See below.
    rot : number
        Rotation angle, used for non-circles.
    scale : number
        Used to scale the y-coordinates.

    Notes
    -----
    Angles to use to densify the circle::

    - 360+ circle
    - 120  triangle
    - 90   square
    - 72   pentagon
    - 60   hexagon
    - 45   octagon
    - etc
    """
    top = circle(outer,
                 clockwise=True,
                 theta=theta,
                 rot=rot,
                 scale=scale,
                 xc=xc,
                 yc=yc)
    if inner == 0.0:
        return top
    bott = circle(inner,
                  clockwise=False,
                  theta=theta,
                  rot=rot,
                  scale=scale,
                  xc=xc,
                  yc=yc)

    pnts = np.concatenate((top, bott), axis=0)
    if asGeo:
        return arrays_to_Geo(pnts, kind=2)
    return pnts
Esempio n. 10
0
def circle(radius=100,
           clockwise=True,
           theta=1,
           rot=0.0,
           scale=1,
           xc=0.0,
           yc=0.0,
           asGeo=True):
    """Produce a circle/ellipse depending on parameters.

    Parameters
    ----------
    radius : number
        In projected units.
    clockwise : boolean
        True for clockwise (outer rings), False for counter-clockwise
        (for inner rings).
    theta : number
        Angle spacing. If theta=1, angles between -180 to 180, are returned
        in 1 degree increments. The endpoint is excluded.
    rot : number
         Rotation angle in degrees... used if scaling is not equal to 1.
    scale : number
         For ellipses, change the scale to <1 or > 1. The resultant
         y-values will favour the x or y-axis depending on the scaling.

    Returns
    -------
    List of coordinates for the circle/ellipse.

    Notes
    -----
    You can also use np.linspace if you want to specify point numbers.

    >>> np.linspace(start, stop, num=50, endpoint=True, retstep=False)
    >>> np.linspace(-180, 180, num=720, endpoint=True, retstep=False)
    """
    if clockwise:
        angles = np.deg2rad(np.arange(180.0, -180.0 - theta, step=-theta))
    else:
        angles = np.deg2rad(np.arange(-180.0, 180.0 + theta, step=theta))
    x_s = radius * np.cos(angles)  # X values
    y_s = radius * np.sin(angles) * scale  # Y values
    pnts = np.array([x_s, y_s]).T
    if rot != 0:
        rot_mat = rot_matrix(angle=rot)
        pnts = (np.dot(rot_mat, pnts.T)).T
    pnts = pnts + [xc, yc]
    if asGeo:
        return arrays_to_Geo(pnts, kind=2)
    return pnts
Esempio n. 11
0
def geojson_Geo(pth, kind=2, info=None, to_origin=False):
    """Convert GeoJSON file to Geo array using `npGeo.arrays_to_Geo`.

    Parameters
    ----------
    pth : string
        Full path to the geojson file.
    kind : integer
        Polygon, Polyline or Point type are identified as either 2, 1, or 0.
    info : text
        Supplementary information.
    """
    coords = load_geojson(pth)
    # a_2d, ift, extents = npGeo.array_IFT(coords)
    return npGeo.arrays_to_Geo(coords,
                               kind=kind,
                               info=info,
                               to_origin=to_origin)
Esempio n. 12
0
def triangle(dx=1,
             dy=1,
             x_cols=1,
             y_rows=1,
             orig_x=0,
             orig_y=1,
             kind=2,
             asGeo=True):
    """Create a row of meshed triangles.

    The triangles are essentially bisected squares and not equalateral.
    The triangles per row will not be terminated in half triangles to
    `square off` the area of coverage.  This is to ensure that all geometries
    have the same area and point construction.

    Parameters
    ----------
    See `rectangles` for shared parameter explanation.
    """
    a, dx, b = dx / 2.0, dx, dx * 1.5
    # X, Y values for a unit triangle, point up and point down
    seedU = np.array([[0.0, 0.0], [a, dy], [dx, 0.0], [0.0, 0.0]])
    seedD = np.array([[a, dy], [b, dy], [dx, 0.0], [a, dy]])
    seed = np.array([seedU, seedD])
    a = [
        seed + [j * dx, i * dy]  # make the shapes
        for i in range(0, y_rows)  # cycle through the rows
        for j in range(0, x_cols)
    ]  # cycle through the columns
    a = np.asarray(a)
    s1, s2, s3, s4 = a.shape
    a = a.reshape(s1 * s2, s3, s4)
    if asGeo:
        frmt = "dx {}, dy {}, x_cols {}, y_rows {}, LB ({},{})"
        txt = frmt.format(dx, dy, x_cols, y_rows, orig_x, orig_y)
        return arrays_to_Geo(a, kind=2, info=txt)
    return a
Esempio n. 13
0
def extent_to_poly(extent, kind=2):
    """Create a polygon/polyline feature from an array of x,y values.

    The array returned is ordered clockwise with the first and last point
    repeated to form a closed-loop.

    Parameters
    ----------
    extent : array-like
        The extent is specified as four float values in the form of
        L(eft), B(ottom), R(ight), T(op) eg. np.array([5, 5, 10, 10]) or a
        pair of points [LB, RT]
    kind : integer
        A value of 1 for a polyline, or 2 for a polygon.
    """
    shp = extent.shape
    if shp not in [(2, 2), (4, )]:
        print("Check the docs...\n{}".format(extent_to_poly.__doc__))
        return None
    L, B, R, T = extent.ravel()
    L, R = min(L, R), max(L, R)
    B, T = min(B, T), max(B, T)
    ext = np.array([[L, B], [L, T], [R, T], [R, B], [L, B]])
    return npGeo.arrays_to_Geo([ext], kind=kind, info="extent to poly")
Esempio n. 14
0
def dissolve(a, asGeo=True):
    """Dissolve polygons sharing edges.

    Parameters
    ----------
    a : Geo array
        A Geo array is required. Use ``arrays_to_Geo`` to convert a list of
        lists/arrays or an object array representing geometry.
    asGeo : boolean
        True, returns a Geo array. False returns a list of arrays.

    Notes
    -----
    >>> from npgeom.npg_plots import plot_polygons  # to plot the geometry

    `_isin_2d_`, `find`, `adjacent` equivalent::

        (b0[:, None] == b1).all(-1).any(-1)
    """

    def _adjacent_(a, b):
        """Check adjacency between 2 polygon shapes."""
        s = np.sum((a[:, None] == b).all(-1).any(-1))
        if s > 0:
            return True
        return False

    def _cycle_(b0, b1):
        """Cycle through the bits."""

        def _find_(a, b):
            """Find.  Abbreviated form of ``adjacent``, to use for slicing."""
            return (a[:, None] == b).all(-1).any(-1)

        idx01 = _find_(b0, b1)
        if idx01.sum() == 0:
            return None
        if idx01[0] == 1:  # you can't split between the first and last pnt.
            b0, b1 = b1, b0
            idx01 = _find_(b0, b1)
        dump = b0[idx01]
        sp0 = np.nonzero(idx01)[0]
        sp1 = np.any(np.isin(b1, dump, invert=True), axis=1)
        z0 = np.array_split(b0, sp0[1:])
        z1 = b1[sp1]
        return np.concatenate((z0[0], z1, z0[-1]), axis=0)

    def _combine_(r, shps):
        """Combine the shapes."""
        missed = []
        processed = False
        for i, shp in enumerate(shps):
            adj = _adjacent_(r, shp[1:-1])  # shp[1:-1])
            if adj:
                new = _cycle_(r, shp[:-1])  # shp[:-1])  ** today
                r = new
                processed = True
            else:
                missed.append(shp)
        if len(shps) == 2 and not processed:
            missed.append(r)
        return r, missed  # done

    # --- check for appropriate Geo array.
    if not hasattr(a, "IFT") or a.is_multipart():
        msg = """function : dissolve
        A `Singlepart` Geo array is required. Use ``arrays_to_Geo`` to convert
        arrays to a Geo array and use ``multipart_to_singlepart`` if needed.
        """
        print(msg)
        return None
    # --- get the outer rings, roll the coordinates and run ``_combine_``.
    a = a.outer_rings(True)
    a = a.roll_shapes()
    a.IFT[:, 0] = np.arange(len(a.IFT))
    out = []
    ids = a.IDs
    shps = a.get_shapes(ids, False)
    r = shps[0]
    missed = shps
    N = len(shps)
    cnt = 0
    while cnt <= N:
        r1, missed1 = _combine_(r, missed[1:])
        if r1 is not None and N >= 0:
            out.append(r1)
        if len(missed1) == 0:
            N = 0
        else:
            N = len(missed1)
            r = missed1[0]
            missed = missed1
        cnt += 1
    # final kick at the can
    if len(out) > 1:
        r, missed = _combine_(out[0], out[1:])
        if missed is not None:
            out = [r] + missed
        else:
            out = r
    if asGeo:
        out = npGeo.arrays_to_Geo(out, 2, "dissolved", False)
        out = npGeo.roll_coords(out)
    return out  # , missed