コード例 #1
0
ファイル: test_join.py プロジェクト: ljwolf/topojson
def test_join_line_ABC_extends_line_BA():
    data = {
        "ba": {"type": "LineString", "coordinates": [[1, 0], [0, 0]]},
        "abc": {"type": "LineString", "coordinates": [[0, 0], [1, 0], [2, 0]]},
    }
    topo = Join(data).to_dict()

    assert geometry.MultiPoint(topo["junctions"]).equals(
        geometry.MultiPoint([(1.0, 0.0), (0.0, 0.0)])
    )
コード例 #2
0
ファイル: test_join.py プロジェクト: Casyfill/topojson
 def test_line_DBC_merge_reversed_line_CBA(self):
     data = {
         "cba": {"type": "LineString", "coordinates": [[2, 0], [1, 0], [0, 0]]},
         "dbc": {"type": "LineString", "coordinates": [[3, 0], [1, 0], [2, 0]]},
     }
     topo = Join(data).to_dict()
     self.assertTrue(
         geometry.MultiPoint(topo["junctions"]).equals(
             geometry.MultiPoint([(2.0, 0.0), (1.0, 0.0), (3.0, 0.0)])
         )
     )
コード例 #3
0
ファイル: test_join.py プロジェクト: Casyfill/topojson
 def test_line_ABD_deviates_line_CBA(self):
     data = {
         "cba": {"type": "LineString", "coordinates": [[2, 0], [1, 0], [0, 0]]},
         "abd": {"type": "LineString", "coordinates": [[0, 0], [1, 0], [3, 0]]},
     }
     topo = Join(data).to_dict()
     self.assertTrue(
         geometry.MultiPoint(topo["junctions"]).equals(
             geometry.MultiPoint([(2.0, 0.0), (0.0, 0.0)])
         )
     )
コード例 #4
0
ファイル: test_join.py プロジェクト: Casyfill/topojson
 def test_line_ABC_extends_line_BC(self):
     data = {
         "abc": {"type": "LineString", "coordinates": [[0, 0], [1, 0], [2, 0]]},
         "bc": {"type": "LineString", "coordinates": [[1, 0], [2, 0]]},
     }
     topo = Join(data).to_dict()
     self.assertTrue(
         geometry.MultiPoint(topo["junctions"]).equals(
             geometry.MultiPoint([(1.0, 0.0), (0.0, 0.0), (2.0, 0.0)])
         )
     )
コード例 #5
0
def alpha_shape(points, alpha):
    """Compute the alpha shape (concave hull) of a set
    of points.
    @param points: Iterable container of points.
    @param alpha: alpha value to influence the
        gooeyness of the border. Smaller numbers
        don't fall inward as much as larger numbers.
        Too large, and you lose everything!
    """
    if len(points) < 4:
        # When you have a triangle, there is no sense
        # in computing an alpha shape.
        return geometry.MultiPoint(
            list(points)).convex_hull, geometry.MultiPoint(list(points))

    def add_edge(edges, edge_points, coords, i, j):
        """Add a line between the i-th and j-th points,
        if not in the list already"""
        if (i, j) in edges or (j, i) in edges:
            # already added
            return
        edges.add((i, j))
        edge_points.append(coords[[i, j]])

    coords = np.array([point.coords[0] for point in points])
    tri = Delaunay(coords)
    edges = set()
    edge_points = []
    # loop over triangles:
    # ia, ib, ic = indices of corner points of the
    # triangle
    for ia, ib, ic in tri.vertices:
        pa = coords[ia]
        pb = coords[ib]
        pc = coords[ic]
        # Lengths of sides of triangle
        a = math.sqrt((pa[0] - pb[0])**2 + (pa[1] - pb[1])**2)
        b = math.sqrt((pb[0] - pc[0])**2 + (pb[1] - pc[1])**2)
        c = math.sqrt((pc[0] - pa[0])**2 + (pc[1] - pa[1])**2)
        # Semiperimeter of triangle
        s = (a + b + c) / 2.0
        # Area of triangle by Heron's formula
        area = math.sqrt(s * (s - a) * (s - b) * (s - c))
        circum_r = a * b * c / (4.0 * area)
        # Here's the radius filter.
        #print circum_r
        if circum_r < 1.0 / alpha:
            add_edge(edges, edge_points, coords, ia, ib)
            add_edge(edges, edge_points, coords, ib, ic)
            add_edge(edges, edge_points, coords, ic, ia)
    m = geometry.MultiLineString(edge_points)
    triangles = list(polygonize(m))
    return cascaded_union(triangles), edge_points
コード例 #6
0
def crowding(track, Roads, buffersize=500, pointsToAdd=1000):
    def getRoadSegments(track, Roads, buffersize=500):    
        Buffer = track.buffer(buffersize)                                       #shapely.ops.cascaded_union(track['geometry'].apply(lambda x: x.buffer(buffersize)))
        return Roads[Roads["geometry"].intersects(Buffer)]

    FakePoints = [point for road in getRoadSegments(track, Roads, buffersize)['geometry'] for point in list(road.coords)]
    try:
        FakePointsToAdd = geometry.MultiPoint(random.sample(FakePoints, pointsToAdd))
    except ValueError:
        FakePointsToAdd = geometry.MultiPoint(FakePoints)
        #print("Not enough points available. Added "+str(len(FakePoints)))
    return track.union(FakePointsToAdd)
コード例 #7
0
    def test_ptconvex2(self):
        print "testing geomutil.ptconvex2"

        points  = shg.MultiPoint([(0, 0), (0, 1), (3.2, 1), (3.2, 0.7), (0.4, 0.7), (0.4, 0)])
        polyg   = Polygon(points)
        cvex,ccave   = polyg.ptconvex2() 
        assert_equal(cvex,[-5] )
        assert_equal(ccave,[-1, -2, -3, -4, -6] )
        points  = shg.MultiPoint([(0, 0), (0, 1), (-3.2, 1), (-3.2, 0.7), (-0.4, 0.7), (-0.4, 0)])
        polyg   = Polygon(points)
        cvex,ccave   = polyg.ptconvex2() 
        assert_equal(cvex,[-5] )
        assert_equal(ccave,[-1, -2, -3, -4, -6] )
コード例 #8
0
    def inc(self, cy):
        """ Returns True if self includes cy 

        """
        npoints = filter(lambda x: x < 0, self.cycle)
        coords = map(lambda x: self.G.pos[x], npoints)
        polyself = geu.Polygon(sh.MultiPoint(tuple(coords)), self.cycle)
        npoints = filter(lambda x: x < 0, cy.cycle)
        coords = map(lambda x: cy.G.pos[x], npoints)
        polycy = geu.Polygon(sh.MultiPoint(tuple(coords)), cy.cycle)
        pdb.set_trace()
        bool = polyself.intersect(polycy)
        return (bool)
コード例 #9
0
def serialize_as_svg(topo_object, separate=False, include_junctions=False):
    from IPython.display import SVG, display
    from shapely import geometry

    keys = topo_object.keys()
    if "arcs" in keys:
        arcs = topo_object["arcs"]
        if arcs:
            # dequantize if quantization is applied
            if "transform" in keys:

                np_arcs = np_array_from_arcs(arcs)

                transform = topo_object["transform"]
                scale = transform["scale"]
                translate = transform["translate"]

                np_arcs = dequantize(np_arcs, scale, translate)
                l_arcs = []
                for ls in np_arcs:
                    l_arcs.append(ls[~np.isnan(ls)[:, 0]].tolist())
                arcs = l_arcs

            arcs = [geometry.LineString(arc) for arc in arcs]

    else:
        arcs = topo_object["linestrings"]

    if separate and not include_junctions:
        for ix, line in enumerate(arcs):
            svg = line._repr_svg_()
            print(ix, line.wkt)
            display(SVG(svg))
    elif separate and include_junctions:
        pts = topo_object["junctions"]
        for ix, line in enumerate(arcs):
            svg = geometry.GeometryCollection(
                [line, geometry.MultiPoint(pts)]
            )._repr_svg_()
            print(ix, line.wkt)
            display(SVG(svg))

    elif not separate and include_junctions:
        pts = topo_object["junctions"]
        display(
            geometry.GeometryCollection(
                [geometry.MultiLineString(arcs), geometry.MultiPoint(pts)]
            )
        )
    else:
        display(geometry.MultiLineString(arcs))
コード例 #10
0
ファイル: test_join.py プロジェクト: pmav99/topojson
 def test_line_ABD_deviates_line_ABC(self):
     data = {
         "abc": {
             "type": "LineString",
             "coordinates": [[0, 0], [1, 0], [2, 0]]
         },
         "abd": {
             "type": "LineString",
             "coordinates": [[0, 0], [1, 0], [3, 0]]
         },
     }
     topo = topojson.join(topojson.extract(data))
     self.assertTrue(
         geometry.MultiPoint(topo["junctions"]).equals(
             geometry.MultiPoint([(0.0, 0.0), (2.0, 0.0)])))
コード例 #11
0
ファイル: test_join.py プロジェクト: olehb/topojson
    def test_join_shared_paths_dict():
        data = {
            "cba": {
                "type": "LineString",
                "coordinates": [[2, 0], [1, 0], [0, 0]]
            },
            "ab": {
                "type": "LineString",
                "coordinates": [[0, 0], [1, 0]]
            },
        }
        topo = Join(data, options={"shared_coords": True}).to_dict()

        assert geometry.MultiPoint(topo["junctions"]).equals(
            geometry.MultiPoint([(0.0, 0.0), (1.0, 0.0)]))
コード例 #12
0
ファイル: test_join.py プロジェクト: olehb/topojson
def test_join_line_BC_start_middle_reversed_line_CBA():
    data = {
        "cba": {
            "type": "LineString",
            "coordinates": [[2, 0], [1, 0], [0, 0]]
        },
        "bc": {
            "type": "LineString",
            "coordinates": [[1, 0], [2, 0]]
        },
    }
    topo = Join(data).to_dict()

    assert geometry.MultiPoint(topo["junctions"]).equals(
        geometry.MultiPoint([(2.0, 0.0), (1.0, 0.0)]))
コード例 #13
0
ファイル: test_join.py プロジェクト: pmav99/topojson
 def test_line_BC_start_middle_reversed_line_CBA(self):
     data = {
         "cba": {
             "type": "LineString",
             "coordinates": [[2, 0], [1, 0], [0, 0]]
         },
         "bc": {
             "type": "LineString",
             "coordinates": [[1, 0], [2, 0]]
         },
     }
     topo = topojson.join(topojson.extract(data))
     self.assertTrue(
         geometry.MultiPoint(topo["junctions"]).equals(
             geometry.MultiPoint([(2.0, 0.0), (1.0, 0.0)])))
コード例 #14
0
ファイル: test_join.py プロジェクト: olehb/topojson
def test_join_true_for_junction_points():
    data = {
        "cba": {
            "type": "LineString",
            "coordinates": [[2, 0], [1, 0], [0, 0]]
        },
        "ab": {
            "type": "LineString",
            "coordinates": [[0, 0], [1, 0]]
        },
    }
    topo = Join(data).to_dict()

    assert geometry.MultiPoint(topo["junctions"]).equals(
        geometry.MultiPoint([(1.0, 0.0), (0.0, 0.0), (2.0, 0.0)]))
コード例 #15
0
ファイル: test_join.py プロジェクト: olehb/topojson
def test_join_line_DBC_merge_line_ABC():
    data = {
        "abc": {
            "type": "LineString",
            "coordinates": [[0, 0], [1, 0], [2, 0]]
        },
        "dbc": {
            "type": "LineString",
            "coordinates": [[3, 0], [1, 0], [2, 0]]
        },
    }
    topo = Join(data).to_dict()

    assert geometry.MultiPoint(topo["junctions"]).equals(
        geometry.MultiPoint([(1.0, 0.0), (2.0, 0.0), (3.0, 0.0), (0.0, 0.0)]))
コード例 #16
0
ファイル: test_gis.py プロジェクト: shawndegroot/salem
    def test_geometry(self):

        import shapely.geometry as shpg

        g = Grid(nxny=(3, 3),
                 dxdy=(1, 1),
                 x0y0=(0, 0),
                 proj=wgs84,
                 pixel_ref='corner')
        p = shpg.Polygon([(1.5, 1.), (2., 1.5), (1.5, 2.), (1., 1.5)])
        o = gis.transform_geometry(p, to_crs=g)
        assert_allclose(p.exterior.coords, o.exterior.coords)

        q = gis.transform_geometry(o, crs=g)
        assert_allclose(p.exterior.coords, q.exterior.coords)

        o = gis.transform_geometry(p, to_crs=g.center_grid)
        totest = np.array(o.exterior.coords) + 0.5
        assert_allclose(p.exterior.coords, totest)

        x, y = g.corner_grid.xy_coordinates
        p = shpg.MultiPoint(
            [shpg.Point(i, j) for i, j in zip(x.flatten(), y.flatten())])
        o = gis.transform_geometry(p, to_crs=g.proj)
        assert_allclose([_p.coords for _p in o], [_p.coords for _p in p])
コード例 #17
0
def compute_convex_hull(feed):
    """
    Return a Shapely Polygon representing the convex hull formed by
    the stops of the given Feed.
    """
    m = sg.MultiPoint(feed.stops[['stop_lon', 'stop_lat']].values)
    return m.convex_hull
コード例 #18
0
 def test_index_multigeom(self):
     c = [(float(x), float(-x)) for x in range(4)]
     g = geometry.MultiPoint(c)
     for i in range(-4, 4):
         self.assertTrue(g[i].equals(geometry.Point(c[i])))
     self.assertRaises(IndexError, lambda: g[4])
     self.assertRaises(IndexError, lambda: g[-5])
コード例 #19
0
ファイル: test_transform.py プロジェクト: blackmad/Shapely
 def test_multipolygon(self):
     g = geometry.MultiPoint([(0, 1), (0, 4)]).buffer(1.0)
     h = transform(lambda x, y, z=None: (x + 1.0, y + 1.0), g)
     self.assertEqual(h.geom_type, 'MultiPolygon')
     self.assertAlmostEqual(g.area, h.area)
     self.assertAlmostEqual(h.centroid.x, 1.0)
     self.assertAlmostEqual(h.centroid.y, 3.5)
コード例 #20
0
ファイル: init_postgis.py プロジェクト: bh0085/dd-analysis
    def alpha_shape(points, alpha):
        """
        Compute the alpha shape (concave hull) of a set
        of points.
        @param points: Iterable container of points.
        @param alpha: alpha value to influence the
            gooeyness of the border. Smaller numbers
            don't fall inward as much as larger numbers.
            Too large, and you lose everything!
        """
        if len(points) < 4:
            # When you have a triangle, there is no sense
            # in computing an alpha shape.
            return geometry.MultiPoint(list(points)).convex_hull

        coords = np.array([point.coords[0] for point in points])
        tri = Delaunay(coords)
        triangles = coords[tri.vertices]
        a = ((triangles[:,0,0] - triangles[:,1,0]) ** 2 + (triangles[:,0,1] - triangles[:,1,1]) ** 2) ** 0.5
        b = ((triangles[:,1,0] - triangles[:,2,0]) ** 2 + (triangles[:,1,1] - triangles[:,2,1]) ** 2) ** 0.5
        c = ((triangles[:,2,0] - triangles[:,0,0]) ** 2 + (triangles[:,2,1] - triangles[:,0,1]) ** 2) ** 0.5
        s = ( a + b + c ) / 2.0
        areas = (s*(s-a)*(s-b)*(s-c)) ** 0.5
        circums = a * b * c / (4.0 * areas)
        filtered = triangles[circums < (1.0 / alpha)]
        edge1 = filtered[:,(0,1)]
        edge2 = filtered[:,(1,2)]
        edge3 = filtered[:,(2,0)]
        edge_points = np.unique(np.concatenate((edge1,edge2,edge3)), axis = 0).tolist()
        m = geometry.MultiLineString(edge_points)
        triangles = list(polygonize(m))
        return cascaded_union(triangles), edge_points
コード例 #21
0
ファイル: process.py プロジェクト: hietakangas-laboratory/LAM
 def project_mps(self, positions, datadir, filename="some.csv"):
     """For the projection of spot coordinates onto the vector."""
     xy_positions = list(
         zip(positions['Position X'], positions['Position Y']))
     # The shapely packages reguires transformation into Multipoints for the
     # projection.
     points = gm.MultiPoint(xy_positions)
     # Find point of projection on the vector.
     positions["VectPoint"] = [
         self.vector.interpolate(self.vector.project(gm.Point(x)))
         for x in points
     ]
     # Find normalized distance (0->1)
     positions["NormDist"] = [
         self.vector.project(x, normalized=True)
         for x in positions["VectPoint"]
     ]
     # Find the bins that the points fall into
     # Determine bins of each feature
     edges = np.linspace(0, 1, Sett.projBins + 1)
     labels = np.arange(0, Sett.projBins)
     positions["DistBin"] = pd.cut(positions["NormDist"],
                                   edges,
                                   labels=labels)
     mp_bin = pd.Series(positions.loc[:, "DistBin"], name=self.name)
     self.data = positions
     self.test_projection(Sett.MPname)
     # Save the obtained data:
     system.save_to_file(mp_bin.astype(int), datadir, filename)
     return mp_bin
コード例 #22
0
    def evaluate_interface(self, positive, negative):
        config = Configuration.config

        plane_samples = self.grid_sample_polygon()

        if len(plane_samples) == 0:
            return False

        mesh_samples = trimesh.transform_points(
            np.column_stack((plane_samples, np.zeros(plane_samples.shape[0]))),
            self.xform)
        pos_dists = positive.nearest.signed_distance(
            mesh_samples + (1 + self.connector_diameter) * self.normal)
        neg_dists = negative.nearest.signed_distance(
            mesh_samples + (1 + self.connector_diameter) * -1 * self.normal)
        # overestimate sqrt(2) to make the radius larger than half the diagonal of a square connector
        pos_valid_mask = pos_dists > 1.5 * self.connector_diameter / 2
        neg_valid_mask = neg_dists > 1.5 * self.connector_diameter / 2
        ch_area_mask = np.logical_or(pos_valid_mask, neg_valid_mask)

        if ch_area_mask.sum() == 0:
            return False

        convex_hull_area = sg.MultiPoint(plane_samples[ch_area_mask]).buffer(
            self.connector_diameter / 2).convex_hull.area
        self.objective = max(
            self.area / convex_hull_area - config.connector_objective_th, 0)
        self.positive_sites = mesh_samples[pos_valid_mask]
        self.negative_sites = mesh_samples[neg_valid_mask]
        self.all_sites = np.concatenate(
            (self.positive_sites, self.negative_sites), axis=0)
        return True
コード例 #23
0
 def get_cluster_centroids(
         self, clustered_guids, none_clustered_guids=None):
     """Get centroids for clustered data"""
     resultshapes_and_meta = list()
     for post_cluster in clustered_guids:
         posts = [self.cleaned_post_dict[x] for x in post_cluster]
         unique_user_count = len(set([post.user_guid for post in posts]))
         # get points and project coordinates to suitable UTM
         points = [geometry.Point(
             self._proj_coords(post.lng, post.lat)
         ) for post in posts]
         point_collection = geometry.MultiPoint(list(points))
         # convex hull enough for calculating centroid
         result_polygon = point_collection.convex_hull
         result_centroid = result_polygon.centroid
         if result_centroid is not None and not result_centroid.is_empty:
             resultshapes_and_meta.append(
                 (result_centroid, unique_user_count)
             )
     if not none_clustered_guids:
         return resultshapes_and_meta
     # noclusterphotos = [cleanedPhotoDict[x] for x in singlePhotoGuidList]
     for no_cluster_post in none_clustered_guids:
         post = self.cleaned_post_dict[no_cluster_post]
         x_point, y_point = self._proj_coords(
             post.lng, post.lat)
         p_center = geometry.Point(x_point, y_point)
         if p_center is not None and not p_center.is_empty:
             resultshapes_and_meta.append((p_center, 1))
     sys.stdout.flush()
     # log.debug(f'{resultshapes_and_meta[:10]}')
     return resultshapes_and_meta
コード例 #24
0
def make_grid(block, step=0.1):
    bbox = block.surface.bounds
    xs = np.arange(bbox[0], bbox[2] + step, step)
    ys = np.arange(bbox[1], bbox[3] + step, step)
    grid = np.array(np.meshgrid(xs, ys)).T.reshape(-1, 2)
    grid = np.round(grid, 2)
    return geometry.MultiPoint(grid)
コード例 #25
0
ファイル: kernels.py プロジェクト: wsteenbeek/PredictCode
 def number_intersecting_pts(self, pt):
     """The number of the sample points which intersect, once centred at
     `pt`."""
     pts = _sgeometry.MultiPoint(self.edge_sample_points(pt))
     pts = pts.intersection(self.transformed_geometry)
     # Seems the fastest method
     return _np.asarray(pts).shape[0]
コード例 #26
0
ファイル: miscellany.py プロジェクト: markongithub/gtfstk
def compute_convex_hull(feed: "Feed") -> Polygon:
    """
    Return a Shapely Polygon representing the convex hull formed by
    the stops of the given Feed.
    """
    m = sg.MultiPoint(feed.stops[["stop_lon", "stop_lat"]].values)
    return m.convex_hull
コード例 #27
0
    def determine_side_order(self, sides):
        """ determine which of the sides is the ventral side based on geometric
        properties and return indices such that the ventral side comes first """
        # define a line connecting both end points
        k1, k2 = self.endpoint_indices
        line = geometry.LineString([self.contour[k1], self.contour[k2]])

        # cut the shape using this line and return the largest part
        parts = self.polygon.difference(line.buffer(0.1))
        if isinstance(parts, geometry.MultiPolygon):
            areas = [part.area for part in parts]
            polygon = parts[np.argmax(areas)].buffer(0.1)
        else:
            polygon = parts

        # measure the fraction of points that lie in the polygon
        fs = []
        for c in sides:
            mp = geometry.MultiPoint(c)
            intersection = mp.intersection(polygon)
            if isinstance(intersection, geometry.Point):
                frac = 1 / len(mp)
            else:
                frac = len(intersection) / len(mp)
            fs.append(frac)

        # return the order in which the sides should be put
        if np.argmax(fs) == 0:
            return (0, 1)
        else:
            return (1, 0)
コード例 #28
0
def nodes_in_ring(list1, list2):
    '''
    Return the coordinates of the nodes in the ring 
    formed by two circles center in the center of mas of the first list 
    and of radius the max distance to all other nodes of list1 for 
    circle 1 and of list 2 for circle 2
    
    Parameters : 
                - list1 : list of coordinates [(x1,y1), (x2,y2), ...]
                
                - list2 : same
    '''
    CM = center_mass(list1)
    
    r1 = dist_max(CM, list1)+0.5
    r2 = dist_max(CM, list2)+0.5
    mp2 = sg.MultiPoint(list2)
    circle1 = sg.Point(CM).buffer(r1)
    circle2 = sg.Point(CM).buffer(r2)
    
    ring = circle2.difference(circle1)
    nodes = ring.intersection(mp2)
    
    ret = []
    for i in range(len(nodes)):
        ret.append([nodes.geoms[i].x , nodes.geoms[i].y])
    
    return ret
コード例 #29
0
ファイル: process.py プロジェクト: hietakangas-laboratory/LAM
    def project_channel(self, channel):
        """For projecting coordinates onto the vector."""
        data = channel.data
        xy_positions = list(zip(data['Position X'], data['Position Y']))
        # Transformation into Multipoints required for projection:
        points = gm.MultiPoint(xy_positions)
        # Find projection distance on the vector.
        proj_vector_dist = [self.vector.project(x) for x in points]
        # Find the exact point of projection
        proj_points = [self.vector.interpolate(p) for p in proj_vector_dist]
        # Find distance between feature and the point of projection
        proj_dist = [p.distance(proj_points[i]) for i, p in enumerate(points)]
        # Find normalized distance (0->1)
        data["NormDist"] = [d / self.vector_length for d in proj_vector_dist]
        # Determine bins of each feature
        edges = np.linspace(0, 1, Sett.projBins + 1)
        labels = np.arange(0, Sett.projBins)
        data["DistBin"] = pd.cut(data["NormDist"],
                                 labels=labels,
                                 bins=edges,
                                 include_lowest=True).astype('int')

        # Assign data to DF and save the dataframe:
        data["VectPoint"] = [(round(p.x, 3), round(p.y, 3))
                             for p in proj_points]
        data["ProjDist"] = proj_dist
        self.data = data
        self.test_projection(channel.name)
        channel_string = f'{channel.name}.csv'
        system.save_to_file(data, self.sampledir, channel_string, append=False)
        return data
コード例 #30
0
def alpha_shape(points, alpha):
    # No alpha shape for a triangle.
    if len(points) < 4:
        return geometry.MultiPoint(list(points)).convex_hull
    coords = np.array([point.coords[0] for point in points])
    tri = Delaunay(coords)
    triangles = coords[tri.vertices]
    a = ((triangles[:, 0, 0] - triangles[:, 1, 0])**2 +
         (triangles[:, 0, 1] - triangles[:, 1, 1])**2)**0.5
    b = ((triangles[:, 1, 0] - triangles[:, 2, 0])**2 +
         (triangles[:, 1, 1] - triangles[:, 2, 1])**2)**0.5
    c = ((triangles[:, 2, 0] - triangles[:, 0, 0])**2 +
         (triangles[:, 2, 1] - triangles[:, 0, 1])**2)**0.5
    s = (a + b + c) / 2.0
    areas = (s * (s - a) * (s - b) * (s - c))**0.5
    circums = a * b * c / (4.0 * areas)
    filtered = triangles[circums < (1.0 / alpha)]
    edge1 = filtered[:, (0, 1)]
    edge2 = filtered[:, (1, 2)]
    edge3 = filtered[:, (2, 0)]
    edge_points = np.unique(np.concatenate((edge1, edge2, edge3)),
                            axis=0).tolist()
    m = geometry.MultiLineString(edge_points)
    triangles = list(polygonize(m))
    return cascaded_union(triangles), edge_points