Ejemplo n.º 1
0
def voronoi_polygons(grid):
    '''
    Creates Voronoi polygons in x,y from a set of points.
    
    Returns:
        cell (list[Cell]): the cells of the graph. A cell has a `center` and a\
            `polygon` list of vertices. An example can be seen below:
    
    >>> g = grid((3, 3), scale=2.5)
    >>> voronoi_polygons(g)
    [Cell(center=Point3d(2.5, 2.5, 0), polygon=[Point3d(3.75, 1.25, 0), Point3d(3.75, 3.75, 0), Point3d(1.25, 3.75, 0), Point3d(1.25, 1.25, 0)])]
    '''
    pyvoronoi_scaling = 100.0
    voro = pv.Pyvoronoi(pyvoronoi_scaling)
    for p in grid:
        voro.AddPoint(p.xy)
    voro.Construct()
    pv_cells = voro.GetCells()

    cells = []
    for i, pv_cell in enumerate(pv_cells):
        if pv_cell.is_open:
            continue
        polygon = []
        for edge_index in pv_cell.edges:
            e = voro.GetEdge(edge_index)
            start = voro.GetVertex(e.start)
            polygon.append(Point3d(start.X, start.Y, grid[pv_cell.site].z))
        cells.append(Cell(
            Point3d(*grid[pv_cell.site]),
            polygon,
        ))
    return cells
Ejemplo n.º 2
0
 def __init__(self, points: list, segments: list, bounding_box_coords: list, scaling_factor=100000.0):
     if pyvoronoi is None:
         raise ImportError("Impossible to use Voronoi utils, `pyvoronoi` package not found.")
     self.pv = pyvoronoi.Pyvoronoi(scaling_factor)
     for p in points:
         self.pv.AddPoint(p)
     for s in segments:
         self.pv.AddSegment(s)
     self.pv.Construct()
     self.discretization_tolerance = 10000 / scaling_factor
     self.bounding_box_coords = bounding_box_coords
Ejemplo n.º 3
0
 def run(self):
     # return [1,2,3]
     pv = pyvoronoi.Pyvoronoi(1)
     pv.AddSegment([[0, 0], [0, 2]])
     pv.AddSegment([[0, 2], [1, 2]])
     pv.AddSegment([[1, 2], [1, 0]])
     pv.AddSegment([[1, 0], [0, 0]])
     pv.Construct()
     edges = pv.GetEdges()
     vertices = pv.GetVertices()
     return vertices
Ejemplo n.º 4
0
    def __get_voronoi(self) -> Tuple[pyvoronoi.Pyvoronoi, List]:
        all_segments = self.__get_segments_for_elements(self.all_elements)
        # Add the page boundary as segments:
        all_segments += [
            [(0, 0), (0, self.page.height)],
            [(0, 0), (self.page.width, 0)],
            [(0, self.page.height), (self.page.width, self.page.height)],
            [(self.page.width, 0), (self.page.width, self.page.height)],
        ]

        pv = pyvoronoi.Pyvoronoi(10)
        for segment in all_segments:
            pv.AddSegment(segment)

        pv.Construct()
        return pv, all_segments
Ejemplo n.º 5
0
def MaximumInscribedCircle(geom):
    '''
        Computer maximum inscribed circle of a polygon by voronoi of its boundary segments with pyvoronoi
    '''

    if not isinstance(geom, Polygon):
        raise ValueError('Must be instance of a Polygon class; found ' +
                         geom.__class__.__name__)

    pv = pyvoronoi.Pyvoronoi(1)
    exterior = geom.exterior
    ptnum = len(exterior.coords)
    coords = exterior.coords[:]
    for i in range(ptnum - 1):
        j = (i + 1) % ptnum
        pv.AddSegment([coords[i], coords[j]])

    for interior in geom.interiors:
        ptnum = len(interior.coords)
        coords = interior.coords[:]
        for i in range(ptnum - 1):
            j = (i + 1) % ptnum
            pv.AddSegment([coords[i], coords[j]])

    pv.Construct()
    vertices = pv.GetVertices()

    radius = 0
    center = None
    for v in vertices:
        pt = Point(v.X, v.Y)
        if (geom.contains(pt)):
            r = pt.distance(geom.boundary)
            if (r > radius):
                radius = r
                center = pt
    return (center.x, center.y, radius)
Ejemplo n.º 6
0
def unsafe_neighbors(shapes, scale=100, border=1):
    # shapes must not intersect.

    pv = pyvoronoi.Pyvoronoi(scale)
    site_to_shape = []

    for i, shape in enumerate(shapes):
        if shape.is_empty:
            continue
        coords = np.asarray(shape.exterior)
        assert np.all(coords[0] == coords[-1])
        for a, b in zip(coords, coords[1:]):
            assert np.any(a != b)
            pv.AddSegment((a, b))
            site_to_shape.append(i)

    minx, miny, maxx, maxy = shape_collection_bounds(shapes, border)
    pv.AddSegment(((minx, miny), (maxx, miny)))
    pv.AddSegment(((maxx, miny), (maxx, maxy)))
    pv.AddSegment(((maxx, maxy), (minx, maxy)))
    pv.AddSegment(((minx, maxy), (minx, miny)))
    site_to_shape.extend([-1, -1, -1, -1])

    pv.Construct()

    edges = pv.GetEdges()
    cells = pv.GetCells()

    graph = nx.Graph()
    graph.add_nodes_from(range(len(cells)))

    def adjacent_cell():
        for edge in edges:
            yield edge.cell, edges[edge.twin].cell

    graph.add_edges_from(adjacent_cell())

    for i, cell in enumerate(cells):
        if cell.is_open or cell.site < 0:
            graph.remove_node(i)
        elif not cell.contains_segment:
            edges = np.array(list(graph.edges(i)))
            nhood = set(edges.flatten()) - set([i])
            nhood = list(nhood)
            for j, x in enumerate(nhood):
                for y in nhood[j + 1:]:
                    graph.add_edge(x, y)

            graph.remove_node(i)

    partitions = [[] for _ in shapes]
    for i in graph.nodes():
        k = site_to_shape[cells[i].site]
        partitions[k].append(i)

    graph = nx.quotient_graph(graph, partitions)

    mapping = dict()
    for group in graph.nodes:
        if group:
            mapping[group] = site_to_shape[cells[next(iter(group))].site]
    graph = nx.relabel_nodes(graph, mapping)

    return graph
Ejemplo n.º 7
0
def main():
    try:
        ##################################################################################
        #READ PARAMETERS
        ##################################################################################
        inpoints = arcpy.GetParameterAsText(0)
        inlines = arcpy.GetParameterAsText(1)
        outWorkspace = arcpy.GetParameterAsText(2)
        outpoints = arcpy.GetParameterAsText(3)
        outsegments = arcpy.GetParameterAsText(4)
        outpolygons = arcpy.GetParameterAsText(5)
        inroads_identifier = arcpy.GetParameterAsText(6)
        arcpy.env.workspace = outWorkspace

        ##################################################################################
        #HARD CODED PARAMETERS
        ##################################################################################
        if arcpy.env.scratchWorkspace is None:
            arcpy.env.scratchWorkspace = r'C:\Users\fancelin\Documents\ArcGIS\Default.gdb'
        factor = 100
        inroads_split_name = "voronoying_lines_split"
        inroads_split_line_name = "voronoying_lines_split_lines"
        inroads_split = "{0}{1}{2}".format(arcpy.env.scratchWorkspace,
                                           os.path.sep, inroads_split_name)
        inroads_split_line = "{0}{1}{2}".format(arcpy.env.scratchWorkspace,
                                                os.path.sep,
                                                inroads_split_line_name)
        spatial_reference = arcpy.Describe(inlines).spatialReference

        ##################################################################################
        #VALIDATION
        ##################################################################################
        arcpy.AddMessage("Validation")
        #Validate license requirements
        validateLicense()

        #Validate lines are provided
        if len(outsegments) == 0:
            raise Exception("Input lines were not provided.")

        #Validate that a line identifier was provided
        if len(inroads_identifier) == 0:
            raise Exception("Input lines identifer was not provided.")

        extents = []
        #Validate input line feature class.
        inlinesBBox = validateInputLineFeatureClass(inlines)
        extents.append(inlinesBBox)
        #Validate input point feature class if required.
        inPointsBBox = validateInputPointFeatureClass(inpoints) if len(
            arcpy.GetParameterAsText(0)) > 0 else None

        ##################################################################################
        #REMOVE FEATURE CLASSES
        ##################################################################################
        for fc in [
                inroads_split, inroads_split_line,
                "{0}{1}{2}".format(outWorkspace, os.path.sep, outpoints),
                "{0}{1}{2}".format(outWorkspace, os.path.sep, outsegments),
                "{0}{1}{2}".format(outWorkspace, os.path.sep, outpolygons)
        ]:
            delFCByPath(fc)

        ##################################################################################
        #COMPUTING THE BOUNDING BOX
        ##################################################################################
        # Instanciate pyvoronoi
        pv = pyvoronoi.Pyvoronoi(factor)
        arcpy.AddMessage("Add points to voronoi")
        pointOIDs = []
        if inPointsBBox != None:
            extents.append(inPointsBBox)
            for point in arcpy.da.SearchCursor(inpoints,
                                               ['SHAPE@X', 'SHAPE@Y', 'OID@']):
                pointOIDs.append(point[2])
                pv.AddPoint([point[0], point[1]])

        arcpy.AddMessage("Computing bounding box outlines")
        finalBBox = mergeExtent(extents)
        finalBBoxExpended = arcpy.Extent(finalBBox.XMin - 1,
                                         finalBBox.YMin - 1,
                                         finalBBox.XMax + 1,
                                         finalBBox.YMax + 1)
        bbox_line = [
            arcpy.Array([
                arcpy.Point(finalBBox.XMin, finalBBox.YMin),
                arcpy.Point(finalBBox.XMax, finalBBox.YMin)
            ]),
            arcpy.Array([
                arcpy.Point(finalBBox.XMin, finalBBox.YMin),
                arcpy.Point(finalBBox.XMin, finalBBox.YMax)
            ]),
            arcpy.Array([
                arcpy.Point(finalBBox.XMax, finalBBox.YMax),
                arcpy.Point(finalBBox.XMin, finalBBox.YMax)
            ]),
            arcpy.Array([
                arcpy.Point(finalBBox.XMax, finalBBox.YMax),
                arcpy.Point(finalBBox.XMax, finalBBox.YMin)
            ]),
            arcpy.Array([
                arcpy.Point(finalBBoxExpended.XMin, finalBBoxExpended.YMin),
                arcpy.Point(finalBBoxExpended.XMax, finalBBoxExpended.YMin)
            ]),
            arcpy.Array([
                arcpy.Point(finalBBoxExpended.XMin, finalBBoxExpended.YMin),
                arcpy.Point(finalBBoxExpended.XMin, finalBBoxExpended.YMax)
            ]),
            arcpy.Array([
                arcpy.Point(finalBBoxExpended.XMax, finalBBoxExpended.YMax),
                arcpy.Point(finalBBoxExpended.XMin, finalBBoxExpended.YMax)
            ]),
            arcpy.Array([
                arcpy.Point(finalBBoxExpended.XMax, finalBBoxExpended.YMax),
                arcpy.Point(finalBBoxExpended.XMax, finalBBoxExpended.YMin)
            ])
        ]
        arcpy.AddMessage("Bounding Box Info: {0},{1} | {2},{3}".format(
            finalBBox.XMin, finalBBox.YMin, finalBBox.XMax, finalBBox.YMax))

        ##################################################################################
        #FORMAT INPUT. NEED TO MAKE SURE LINE ARE SPLIT AT VERTICES AND THAT THERE ARE NO OVERLAPS
        ##################################################################################
        arcpy.AddMessage("Format lines")
        arcpy.AddMessage("Split lines at vertices")
        arcpy.SplitLine_management(in_features=inlines,
                                   out_feature_class=inroads_split)

        arcpy.AddMessage("Add bounding box")
        with arcpy.da.InsertCursor(inroads_split,
                                   ['SHAPE@', inroads_identifier]) as op:
            for pointArray in bbox_line:
                arcpy.AddMessage("{0},{1} - {2},{3}".format(
                    pointArray[0].X, pointArray[0].Y, pointArray[1].X,
                    pointArray[1].Y))
                op.insertRow([arcpy.Polyline(pointArray), None])
        del op

        arcpy.AddMessage("Split lines at intersections")
        arcpy.FeatureToLine_management(inroads_split, inroads_split_line, '#',
                                       'ATTRIBUTES')

        ##################################################################################
        #SEND LINE INPUT TO VORONOI AND CONSTRUCT THE GRAPH
        ##################################################################################
        arcpy.AddMessage("Add lines to voronoi")
        lineIds = []
        for road in arcpy.da.SearchCursor(
                inroads_split_line,
            ['SHAPE@', 'OID@', 'SHAPE@LENGTH', inroads_identifier]):
            if (road[2] > 0):
                lineIds.append(road[3])
                pv.AddSegment([[road[0].firstPoint.X, road[0].firstPoint.Y],
                               [road[0].lastPoint.X, road[0].lastPoint.Y]])

        arcpy.AddMessage("Construct voronoi")
        pv.Construct()
        cells = pv.GetCells()
        edges = pv.GetEdges()
        vertices = pv.GetVertices()

        ##################################################################################
        #CREATE THE OUTPUT FEATURE CLASSES
        ##################################################################################
        arcpy.AddMessage("Construct output point feature class")
        if len(outpoints) > 0:
            arcpy.CreateFeatureclass_management(
                outWorkspace,
                outpoints,
                'POINT',
                spatial_reference=spatial_reference)
            arcpy.AddField_management(outpoints, 'IDENTIFIER', "LONG")
            fields = ['IDENTIFIER', 'SHAPE@X', 'SHAPE@Y']
            cursor = arcpy.da.InsertCursor(outpoints, fields)
            for vIndex in range(len(vertices)):
                v = vertices[vIndex]
                cursor.insertRow([vIndex, v.X, v.Y])

        arcpy.AddMessage("Construct output segment feature class")
        if len(outsegments) > 0:
            arcpy.CreateFeatureclass_management(
                outWorkspace,
                outsegments,
                'POLYLINE',
                spatial_reference=spatial_reference)
            arcpy.AddField_management(outsegments, 'EdgeIndex', "LONG")
            arcpy.AddField_management(outsegments, 'Start', "LONG")
            arcpy.AddField_management(outsegments, 'End', "LONG")
            arcpy.AddField_management(outsegments, 'IsLinear', "SHORT")
            arcpy.AddField_management(outsegments, 'IsPrimary', "SHORT")
            arcpy.AddField_management(outsegments, 'Site1', "LONG")
            arcpy.AddField_management(outsegments, 'Site2', "LONG")
            arcpy.AddField_management(outsegments, 'Cell', "LONG")
            arcpy.AddField_management(outsegments, 'Twin', "LONG")
            arcpy.AddField_management(outsegments, 'FROM_X', "DOUBLE")
            arcpy.AddField_management(outsegments, 'FROM_Y', "DOUBLE")
            arcpy.AddField_management(outsegments, 'TO_X', "DOUBLE")
            arcpy.AddField_management(outsegments, 'TO_Y', "DOUBLE")

            fields = [
                'EdgeIndex', 'Start', 'End', 'IsLinear', 'IsPrimary', 'Site1',
                'Site2', 'Cell', 'Twin', 'FROM_X', 'FROM_Y', 'TO_X', 'TO_Y',
                'SHAPE@'
            ]
            cursor = arcpy.da.InsertCursor(outsegments, fields)
            for cIndex in range(len(cells)):
                cell = cells[cIndex]
                if cell.is_open == False:
                    if (cIndex % 5000 == 0 and cIndex > 0):
                        arcpy.AddMessage("Cell Index: {0}".format(cIndex))

                    for i in range(len(cell.edges)):
                        e = edges[cell.edges[i]]
                        startVertex = vertices[e.start]
                        endVertex = vertices[e.end]
                        max_distance = Distance(
                            [startVertex.X, startVertex.Y],
                            [endVertex.X, endVertex.Y]) / 10
                        array = arcpy.Array()
                        if startVertex != -1 and endVertex != -1:
                            if (e.is_linear == True):
                                array = arcpy.Array([
                                    arcpy.Point(startVertex.X, startVertex.Y),
                                    arcpy.Point(endVertex.X, endVertex.Y)
                                ])

                            else:
                                try:
                                    points = pv.DiscretizeCurvedEdge(
                                        cell.edges[i], max_distance,
                                        1 / factor)
                                    for p in points:
                                        array.append(arcpy.Point(p[0], p[1]))
                                except pyvoronoi.FocusOnDirectixException:
                                    arcpy.AddMessage(
                                        "FocusOnDirectixException at: {5}. The drawing has been defaulted from a curved line to a straight line. Length {0} - From: {1}, {2} To: {3}, {4}"
                                        .format(max_distance, startVertex.X,
                                                startVertex.Y, endVertex.X,
                                                endVertex.Y, cell.edges[i]))
                                    array = arcpy.Array([
                                        arcpy.Point(startVertex.X,
                                                    startVertex.Y),
                                        arcpy.Point(endVertex.X, endVertex.Y)
                                    ])

                                except pyvoronoi.UnsolvableParabolaEquation:
                                    edge = pv.outputEdges[cell.edges[i]]
                                    sites = pv.ReturnCurvedSiteInformation(
                                        edge)
                                    pointSite = sites[0]
                                    segmentSite = sites[1]
                                    edgeStartVertex = pv.outputVertices[
                                        edge.start]
                                    edgeEndVertex = pv.outputVertices[edge.end]

                                    print "Input Point: {0}".format(pointSite)
                                    print "Input Segment: {0}".format(
                                        segmentSite)
                                    print "Parabola Start: {0}".format(
                                        [edgeStartVertex.X, edgeStartVertex.Y])
                                    print "Parabola End: {0}".format(
                                        [edgeEndVertex.X, edgeEndVertex.Y])
                                    print "Distance: {0}".format(max_distance)

                                    arcpy.AddMessage(
                                        "UnsolvableParabolaEquation exception at: {5}. The drawing has been defaulted from a curved line to a straight line. Length {0} - From: {1}, {2} To: {3}, {4}"
                                        .format(max_distance, startVertex.X,
                                                startVertex.Y, endVertex.X,
                                                endVertex.Y, cell.edges[i]))
                                    array = arcpy.Array([
                                        arcpy.Point(startVertex.X,
                                                    startVertex.Y),
                                        arcpy.Point(endVertex.X, endVertex.Y)
                                    ])

                            polyline = arcpy.Polyline(array)
                            cursor.insertRow(
                                (cell.edges[i], e.start, e.end, e.is_linear,
                                 e.is_primary, e.site1, e.site2, e.cell,
                                 e.twin, startVertex.X, startVertex.Y,
                                 endVertex.X, endVertex.Y, polyline))

        arcpy.AddMessage("Construct output cells feature class")
        if len(outpolygons) > 0:
            arcpy.CreateFeatureclass_management(
                outWorkspace,
                outpolygons,
                'POLYGON',
                spatial_reference=spatial_reference)
            arcpy.AddField_management(outpolygons, 'CELL_ID', "LONG")
            arcpy.AddField_management(outpolygons, 'CONTAINS_POINT', "SHORT")
            arcpy.AddField_management(outpolygons, 'CONTAINS_SEGMENT', "SHORT")
            arcpy.AddField_management(outpolygons, 'SITE', "LONG")
            arcpy.AddField_management(outpolygons, 'SOURCE_CATEGORY', "SHORT")
            arcpy.AddField_management(outpolygons, 'INPUT_TYPE', "TEXT")
            arcpy.AddField_management(outpolygons, 'INPUT_ID', "LONG")
            fields = [
                'CELL_ID', 'CONTAINS_POINT', 'CONTAINS_SEGMENT', 'SHAPE@',
                'SITE', 'SOURCE_CATEGORY', 'INPUT_TYPE', 'INPUT_ID'
            ]
            cursor = arcpy.da.InsertCursor(outpolygons, fields)
            for cIndex in range(len(cells)):
                cell = cells[cIndex]
                if cell.is_open == False:
                    if (cIndex % 5000 == 0 and cIndex > 0):
                        arcpy.AddMessage("Cell Index: {0}".format(cIndex))
                    pointArray = arcpy.Array()
                    for vIndex in cell.vertices:
                        pointArray.add(
                            arcpy.Point(vertices[vIndex].X,
                                        vertices[vIndex].Y))
                    input_type = None
                    input_id = None
                    if cell.site >= len(pointOIDs):
                        input_type = "LINE"
                        input_id = lineIds[cell.site - len(pointOIDs)]
                    else:
                        input_type = "POINT"
                        input_id = pointOIDs[cell.site]
                    polygon = arcpy.Polygon(pointArray)
                    cursor.insertRow(
                        (cell.cell_identifier, cell.contains_point,
                         cell.contains_segment, polygon, cell.site,
                         cell.source_category, input_type, input_id))
            del cursor

    except Exception:
        tb = sys.exc_info()[2]
        tbInfo = traceback.format_tb(tb)[-1]
        arcpy.AddError('PYTHON ERRORS:\n%s\n%s: %s\n' %
                       (tbInfo, sys.exc_type, sys.exc_value))
        # print('PYTHON ERRORS:\n%s\n%s: %s\n' %
        #                 (tbInfo, _sys.exc_type, _sys.exc_value))
        arcpy.AddMessage('PYTHON ERRORS:\n%s\n%s: %s\n' %
                         (tbInfo, sys.exc_type, sys.exc_value))
        gp_errors = arcpy.GetMessages(2)
        if gp_errors:
            arcpy.AddError('GP ERRORS:\n%s\n' % gp_errors)
Ejemplo n.º 8
0
def main():
    try:
        ##################################################################################
        # READ PARAMETERS
        ##################################################################################
        inpoints = arcpy.GetParameterAsText(0)
        inlines = arcpy.GetParameterAsText(1)
        out_workspace = arcpy.GetParameterAsText(2)
        outpoints = arcpy.GetParameterAsText(3)
        outsegments = arcpy.GetParameterAsText(4)
        outpolygons = arcpy.GetParameterAsText(5)
        inroads_identifier = arcpy.GetParameterAsText(6)
        curve_ratio_txt = arcpy.GetParameterAsText(7)
        arcpy.env.workspace = out_workspace

        ##################################################################################
        # HARD CODED PARAMETERS
        ##################################################################################
        if arcpy.env.scratchWorkspace is None:
            arcpy.env.scratchWorkspace = r'in_memory'
        factor = 100
        default_curve_ratio = 10
        inroads_split_name = "voronoying_lines_split"
        inroads_split_line_name = "voronoying_lines_split_lines"
        inroads_split = os.path.join(arcpy.env.scratchWorkspace,
                                     inroads_split_name)
        inroads_split_line = os.path.join(arcpy.env.scratchWorkspace,
                                          inroads_split_line_name)
        spatial_reference = arcpy.Describe(inlines).spatialReference

        ##################################################################################
        # VALIDATION
        ##################################################################################
        arcpy.AddMessage("Validation")
        # Validate license requirements
        validate_license()

        # Validate lines are provided
        if len(outsegments) == 0:
            raise Exception("Input lines were not provided.")

        # Validate that a line identifier was provided
        if len(inroads_identifier) == 0:
            raise Exception("Input lines identifier was not provided.")

        extents = []
        # Validate input line feature class.
        input_lines_bbox = validate_input_line_feature_class(inlines)
        extents.append(input_lines_bbox)
        # Validate input point feature class if required.
        input_points_bbox = validate_input_point_feature_class(
            inpoints) if len(arcpy.GetParameterAsText(0)) > 0 else None

        curve_ratio = default_curve_ratio
        if curve_ratio_txt is None and curve_ratio_txt == '' and curve_ratio_txt == '#':
            curve_ratio = float(curve_ratio_txt)

        if curve_ratio < 1:
            raise Exception(
                'Invalid curve ratio. It must be greater than 1. Current value: {}'
                .format(curve_ratio))

        ##################################################################################
        # REMOVE FEATURE CLASSES
        ##################################################################################
        for fc in [
                inroads_split, inroads_split_line,
                os.path.join(out_workspace, outpoints),
                os.path.join(out_workspace, outsegments),
                os.path.join(out_workspace, outpolygons)
        ]:
            delete_feature_class(fc)

        ##################################################################################
        # COMPUTING THE BOUNDING BOX
        ##################################################################################
        # Invoke pyvoronoi
        pv = pyvoronoi.Pyvoronoi(factor)
        arcpy.AddMessage("Add points to voronoi")
        point_identifiers = []
        if input_points_bbox is not None:
            extents.append(input_points_bbox)
            for point in arcpy.da.SearchCursor(inpoints,
                                               ['SHAPE@X', 'SHAPE@Y', 'OID@']):
                point_identifiers.append(point[2])
                pv.AddPoint([point[0], point[1]])

        arcpy.AddMessage("Computing bounding box outlines")
        final_bounding_box = merge_extent(extents)
        final_bounding_box_expended = arcpy.Extent(final_bounding_box.XMin - 1,
                                                   final_bounding_box.YMin - 1,
                                                   final_bounding_box.XMax + 1,
                                                   final_bounding_box.YMax + 1)

        bbox_line = [
            arcpy.Array([
                arcpy.Point(final_bounding_box.XMin, final_bounding_box.YMin),
                arcpy.Point(final_bounding_box.XMax, final_bounding_box.YMin)
            ]),
            arcpy.Array([
                arcpy.Point(final_bounding_box.XMin, final_bounding_box.YMin),
                arcpy.Point(final_bounding_box.XMin, final_bounding_box.YMax)
            ]),
            arcpy.Array([
                arcpy.Point(final_bounding_box.XMax, final_bounding_box.YMax),
                arcpy.Point(final_bounding_box.XMin, final_bounding_box.YMax)
            ]),
            arcpy.Array([
                arcpy.Point(final_bounding_box.XMax, final_bounding_box.YMax),
                arcpy.Point(final_bounding_box.XMax, final_bounding_box.YMin)
            ]),
            arcpy.Array([
                arcpy.Point(final_bounding_box_expended.XMin,
                            final_bounding_box_expended.YMin),
                arcpy.Point(final_bounding_box_expended.XMax,
                            final_bounding_box_expended.YMin)
            ]),
            arcpy.Array([
                arcpy.Point(final_bounding_box_expended.XMin,
                            final_bounding_box_expended.YMin),
                arcpy.Point(final_bounding_box_expended.XMin,
                            final_bounding_box_expended.YMax)
            ]),
            arcpy.Array([
                arcpy.Point(final_bounding_box_expended.XMax,
                            final_bounding_box_expended.YMax),
                arcpy.Point(final_bounding_box_expended.XMin,
                            final_bounding_box_expended.YMax)
            ]),
            arcpy.Array([
                arcpy.Point(final_bounding_box_expended.XMax,
                            final_bounding_box_expended.YMax),
                arcpy.Point(final_bounding_box_expended.XMax,
                            final_bounding_box_expended.YMin)
            ])
        ]

        arcpy.AddMessage("Bounding Box Info: {0},{1} | {2},{3}".format(
            final_bounding_box.XMin, final_bounding_box.YMin,
            final_bounding_box.XMax, final_bounding_box.YMax))

        ##################################################################################
        # FORMAT INPUT. NEED TO MAKE SURE LINE ARE SPLIT AT VERTICES AND THAT THERE ARE NO OVERLAPS
        ##################################################################################
        arcpy.AddMessage("Format lines")
        arcpy.AddMessage("Split lines at vertices")
        arcpy.SplitLine_management(in_features=inlines,
                                   out_feature_class=inroads_split)

        arcpy.AddMessage("Add bounding box")
        with arcpy.da.InsertCursor(inroads_split,
                                   ['SHAPE@', inroads_identifier]) as op:
            for pointArray in bbox_line:
                arcpy.AddMessage("{0},{1} - {2},{3}".format(
                    pointArray[0].X, pointArray[0].Y, pointArray[1].X,
                    pointArray[1].Y))
                op.insertRow([arcpy.Polyline(pointArray), None])
        del op

        arcpy.AddMessage("Split lines at intersections")
        arcpy.FeatureToLine_management(inroads_split, inroads_split_line, '#',
                                       'ATTRIBUTES')

        ##################################################################################
        # SEND LINE INPUT TO VORONOI AND CONSTRUCT THE GRAPH
        ##################################################################################
        arcpy.AddMessage("Add lines to voronoi")
        line_identifier = []
        for road in arcpy.da.SearchCursor(
                inroads_split_line,
            ['SHAPE@', 'OID@', 'SHAPE@LENGTH', inroads_identifier]):
            if road[2] > 0:
                line_identifier.append(road[3])
                pv.AddSegment([[road[0].firstPoint.X, road[0].firstPoint.Y],
                               [road[0].lastPoint.X, road[0].lastPoint.Y]])

        arcpy.AddMessage("Construct voronoi")
        pv.Construct()
        count_cells = pv.CountCells()

        ##################################################################################
        # CREATE THE OUTPUT FEATURE CLASSES
        ##################################################################################
        arcpy.AddMessage("Construct output point feature class")
        if len(outpoints) > 0:
            arcpy.CreateFeatureclass_management(
                out_workspace,
                outpoints,
                'POINT',
                spatial_reference=spatial_reference)
            arcpy.AddField_management(outpoints, 'IDENTIFIER', "LONG")
            fields = ['IDENTIFIER', 'SHAPE@X', 'SHAPE@Y']
            with arcpy.da.InsertCursor(outpoints, fields) as cursor:
                count_vertex = pv.CountVertices()
                for vIndex in xrange(count_vertex):
                    v = pv.GetVertex(vIndex)
                    cursor.insertRow([vIndex, v.X, v.Y])

        arcpy.AddMessage("Construct output segment feature class")
        if len(outsegments) > 0:
            arcpy.CreateFeatureclass_management(
                out_workspace,
                outsegments,
                'POLYLINE',
                spatial_reference=spatial_reference)
            arcpy.AddField_management(outsegments, 'EdgeIndex', "LONG")
            arcpy.AddField_management(outsegments, 'Start', "LONG")
            arcpy.AddField_management(outsegments, 'End', "LONG")
            arcpy.AddField_management(outsegments, 'IsLinear', "SHORT")
            arcpy.AddField_management(outsegments, 'IsPrimary', "SHORT")
            arcpy.AddField_management(outsegments, 'Cell', "LONG")
            arcpy.AddField_management(outsegments, 'Twin', "LONG")

            fields = [
                'EdgeIndex', 'Start', 'End', 'IsLinear', 'IsPrimary', 'Cell',
                'Twin', 'SHAPE@'
            ]
            with arcpy.da.InsertCursor(outsegments, fields) as cursor:
                for cIndex in xrange(count_cells):
                    cell = pv.GetCell(cIndex)
                    if not cell.is_open:
                        if cIndex % 5000 == 0 and cIndex > 0:
                            arcpy.AddMessage("Cell Index: {0}".format(cIndex))

                        for i in range(len(cell.edges)):
                            e = pv.GetEdge(cell.edges[i])
                            start_vertex = pv.GetVertex(e.start)
                            end_vertex = pv.GetVertex(e.end)
                            max_distance = pyvoronoi.Distance(
                                [start_vertex.X, start_vertex.Y],
                                [end_vertex.X, end_vertex.Y]) / 10
                            array = arcpy.Array()
                            if start_vertex != -1 and end_vertex != -1:
                                if e.is_linear:
                                    array = arcpy.Array([
                                        arcpy.Point(start_vertex.X,
                                                    start_vertex.Y),
                                        arcpy.Point(end_vertex.X, end_vertex.Y)
                                    ])

                                else:
                                    try:
                                        points = pv.DiscretizeCurvedEdge(
                                            cell.edges[i], max_distance,
                                            1 / curve_ratio)
                                        for p in points:
                                            array.append(
                                                arcpy.Point(p[0], p[1]))

                                    except pyvoronoi.FocusOnDirectixException:
                                        arcpy.AddMessage(
                                            "FocusOnDirectixException at: {5}. The drawing has been defaulted from a curved line to a straight line. Length {0} - From: {1}, {2} To: {3}, {4}"
                                            .format(max_distance,
                                                    start_vertex.X,
                                                    start_vertex.Y,
                                                    end_vertex.X, end_vertex.Y,
                                                    cell.edges[i]))
                                        array = arcpy.Array([
                                            arcpy.Point(
                                                start_vertex.X,
                                                start_vertex.Y),
                                            arcpy.Point(
                                                end_vertex.X, end_vertex.Y)
                                        ])

                                    except pyvoronoi.UnsolvableParabolaEquation:
                                        edge = pv.outputEdges[cell.edges[i]]
                                        sites = pv.ReturnCurvedSiteInformation(
                                            edge)
                                        point_site = sites[0]
                                        segment_site = sites[1]
                                        edge_start_vertex = pv.outputVertices[
                                            edge.start]
                                        edge_end_vertex = pv.outputVertices[
                                            edge.end]

                                        arcpy.AddWarning(
                                            "Input Point: {0}".format(
                                                point_site))
                                        arcpy.AddWarning(
                                            "Input Segment: {0}".format(
                                                segment_site))
                                        arcpy.AddWarning(
                                            "Parabola Start: {0}".format([
                                                edge_start_vertex.X,
                                                edge_start_vertex.Y
                                            ]))
                                        arcpy.AddWarning(
                                            "Parabola End: {0}".format([
                                                edge_end_vertex.X,
                                                edge_end_vertex.Y
                                            ]))

                                        arcpy.AddWarning(
                                            "Distance: {0}".format(
                                                max_distance))

                                        arcpy.AddWarning(
                                            "UnsolvableParabolaEquation exception at: {5}. The drawing has been defaulted from a curved line to a straight line. Length {0} - From: {1}, {2} To: {3}, {4}"
                                            .format(max_distance,
                                                    start_vertex.X,
                                                    start_vertex.Y,
                                                    end_vertex.X, end_vertex.Y,
                                                    cell.edges[i]))

                                        array = arcpy.Array([
                                            arcpy.Point(
                                                start_vertex.X,
                                                start_vertex.Y),
                                            arcpy.Point(
                                                end_vertex.X, end_vertex.Y)
                                        ])

                                    except Exception as e:
                                        print "Exception happened at cell '{0}'".format(
                                            i)
                                        raise e

                                polyline = arcpy.Polyline(array)
                                cursor.insertRow(
                                    (cell.edges[i], e.start, e.end,
                                     e.is_linear, e.is_primary, e.cell, e.twin,
                                     polyline))

        arcpy.AddMessage("Construct output cells feature class")
        if len(outpolygons) > 0:
            arcpy.CreateFeatureclass_management(
                out_workspace,
                outpolygons,
                'POLYGON',
                spatial_reference=spatial_reference)
            arcpy.AddField_management(outpolygons, 'CELL_ID', "LONG")
            arcpy.AddField_management(outpolygons, 'CONTAINS_POINT', "SHORT")
            arcpy.AddField_management(outpolygons, 'CONTAINS_SEGMENT', "SHORT")
            arcpy.AddField_management(outpolygons, 'SITE', "LONG")
            arcpy.AddField_management(outpolygons, 'SOURCE_CATEGORY', "SHORT")
            arcpy.AddField_management(outpolygons, 'INPUT_TYPE', "TEXT")
            arcpy.AddField_management(outpolygons, 'INPUT_ID', "LONG")
            fields = [
                'CELL_ID', 'CONTAINS_POINT', 'CONTAINS_SEGMENT', 'SHAPE@',
                'SITE', 'SOURCE_CATEGORY', 'INPUT_TYPE', 'INPUT_ID'
            ]
            with arcpy.da.InsertCursor(outpolygons, fields) as cursor:
                for cIndex in xrange(count_cells):
                    try:
                        cell = pv.GetCell(cIndex)
                        if not cell.is_open and not cell.is_degenerate:

                            if cIndex % 5000 == 0 and cIndex > 0:
                                arcpy.AddMessage(
                                    "Cell Index: {0}".format(cIndex))

                            cell_points_array = arcpy.Array()
                            previous_vertex_index = -1
                            for edge_index in cell.edges:
                                e = pv.GetEdge(edge_index)
                                start_vertex = pv.GetVertex(e.start)
                                end_vertex = pv.GetVertex(e.end)
                                max_distance = pyvoronoi.Distance(
                                    [start_vertex.X, start_vertex.Y],
                                    [end_vertex.X, end_vertex.Y]) / 10

                                points = []
                                if e.is_linear:
                                    points.append(
                                        [start_vertex.X, start_vertex.Y])
                                    points.append([end_vertex.X, end_vertex.Y])
                                else:
                                    try:
                                        curved_points = pv.DiscretizeCurvedEdge(
                                            edge_index, max_distance,
                                            1 / curve_ratio)
                                        points.extend(curved_points)

                                    except Exception as e:
                                        arcpy.AddError(
                                            "Exception happened at cell '{0}'".
                                            format(i))
                                        points.append(
                                            [start_vertex.X, start_vertex.Y])
                                        points.append(
                                            [end_vertex.X, end_vertex.Y])

                                start_index = 1 if previous_vertex_index == e.start else 0
                                for index in range(start_index, len(points)):
                                    point = points[index]
                                    cell_points_array.append(
                                        arcpy.Point(point[0], point[1]))

                            input_type = 'LINE' if cell.site >= len(
                                point_identifiers) else 'POINT'
                            input_id = line_identifier[
                                cell.site -
                                len(point_identifiers)] if cell.site >= len(
                                    point_identifiers) else point_identifiers[
                                        cell.site]

                            polygon = arcpy.Polygon(cell_points_array)
                            cursor.insertRow(
                                (cell.cell_identifier, cell.contains_point,
                                 cell.contains_segment, polygon, cell.site,
                                 cell.source_category, input_type, input_id))
                    except Exception as e:
                        print "Failed on cell {0}".format(cIndex)
                        raise Exception(e)

    except Exception:
        tb = sys.exc_info()[2]
        tbInfo = traceback.format_tb(tb)[-1]
        arcpy.AddError('PYTHON ERRORS:\n%s\n%s: %s\n' %
                       (tbInfo, sys.exc_type, sys.exc_value))

        arcpy.AddMessage('PYTHON ERRORS:\n%s\n%s: %s\n' %
                         (tbInfo, sys.exc_type, sys.exc_value))
        gp_errors = arcpy.GetMessages(2)
        if gp_errors:
            arcpy.AddError('GP ERRORS:\n%s\n' % gp_errors)