コード例 #1
0
def voronoi(
    points: Union[FeatureCollection, List], bbox: Optional[list] = None
) -> Feature:
    """Takes a FeatureCollection of points, and a bounding box,
    and returns a FeatureCollection of Voronoi polygons.

    :param points: To find the Voronoi polygons around. Points should be either
        FeatureCollection of points or list of points.
    :param bbox: A bounding box to clip.
    :return: A GeoJSON Feature.

    Example:
    >>> from turfpy.transformation import voronoi

    >>> points = [
    ... [-66.9703, 40.3183],
    ... [-63.7763, 40.4500],
    ... [-65.4196, 42.13985310302137],
    ... [-69.5813, 43.95405461286195],
    ... [-65.66337553550034, 55.97088945355232],
    ... [-60.280418548905, 56.240669185466146],
    ... [-68.5129561347689, 50.12984589640148],
    ... [-64.2393519226657, 59.66235385923687],
    ... ]
    >>> bbox = [-70, 40, -60, 60]
    >>> voronoi(points, bbox)
    """
    if isinstance(points, FeatureCollection):
        coords = []
        for feature in points["features"]:
            coords.append(feature["features"][0]["geometry"]["coordinates"])
        points = np.array(coords)
    elif isinstance(points, list):
        points = np.array(points)
    else:
        raise ValueError(
            "points should be either FeatureCollection of points of List of Points"
        )
    vor = Voronoi(points)
    lines = [
        ShapelyLineString(vor.vertices[line])
        for line in vor.ridge_vertices
        if -1 not in line
    ]

    convex_hull = MultiPoint([Point(i) for i in points]).convex_hull.buffer(2)
    result = MultiPolygon([poly.intersection(convex_hull) for poly in polygonize(lines)])
    result = MultiPolygon(
        [p for p in result] + [p for p in convex_hull.difference(unary_union(result))]
    )
    if bbox is not None:
        w, s, e, n = bbox
        cliped_result = clip_by_rect(result, w, s, e, n)
        return Feature(geometry=cliped_result)
    return Feature(geometry=result)
コード例 #2
0
def is_visible(player: Coordinates, tile: Coordinates,
               blocking_tiles: List[Coordinates]) -> bool:
    # take the convex hull of the player's eyes and the corners of the tile
    fov_polygon = MultiPoint([
        (player.x + 0.5, player.y + 0.5),
        (tile.x, tile.y),
        (tile.x + 1, tile.y),
        (tile.x + 1, tile.y + 1),
        (tile.x, tile.y + 1),
    ]).convex_hull
    # carve out all the blocking tiles
    for bt in blocking_tiles:
        blocking_box = box(bt.x, bt.y, bt.x + 1, bt.y + 1)
        fov_polygon = fov_polygon.difference(blocking_box)
        if isinstance(fov_polygon, MultiPolygon):
            return False
    return True
コード例 #3
0
        x.get_string(sortby="Distância Euclidiana", reversesort=True))
    export_to_pdf(header, data, currentProvider.name)

    vor = Voronoi(np.array(points))

    lines = [
        LineString(vor.vertices[line]) for line in vor.ridge_vertices
        if -1 not in line
    ]

    convex_hull = MultiPoint([Point(i) for i in points]).convex_hull.buffer(2)
    result = MultiPolygon(
        [poly.intersection(convex_hull) for poly in polygonize(lines)])
    result = MultiPolygon(
        [p for p in result] +
        [p for p in convex_hull.difference(unary_union(result))])

    plt.plot(float(currentProvider.longitude), float(currentProvider.latitude),
             'ko')
    plt.text(float(currentProvider.longitude),
             float(currentProvider.latitude) + 0.01,
             'Fornecedor ' + currentProvider.name,
             horizontalalignment='left',
             color='black')
    plt.axis('equal')
    plt.xlim(vor.min_bound[0] + 0.01, vor.max_bound[0] + 0.01)
    plt.ylim(vor.min_bound[1] + 0.01, vor.max_bound[1] + 0.01)

    for r in result:
        plt.fill(*zip(*np.array(
            list(
コード例 #4
0
#0.23s

vor = Voronoi(low_points)
#0.85s

#CUTTING the global Voronoi tassellation to avoid divergences
#TO DO: it works but I don't know what it really does
lines = [
    LineString(vor.vertices[line]) for line in vor.ridge_vertices
    if -1 not in line
]
convex_hull = MultiPoint([Point(i) for i in low_points]).convex_hull.buffer(2)
tassel = MultiPolygon(
    [poly.intersection(convex_hull) for poly in polygonize(lines)])
tassel = MultiPolygon([p for p in tassel] +
                      [p for p in convex_hull.difference(unary_union(tassel))])
#14.52 s

#%% CREATING THE CIRCLES AND PLOTTING THE VORONOI TASSELLATION WITH INTERECTIONS ~ 300 s
#Dictionary with the identity of all the circles in the image
circles_id = [{
    'r': 10,
    'c': (40, 30)
}, {
    'r': 5,
    'c': (70, 90)
}, {
    'r': 15,
    'c': (20, 60)
}, {
    'r': 13,
コード例 #5
0
print('TILE 1')
# tile we want to check for visibility - should be blocked
tile1_x, tile1_y = 7.0, 7.0
# take the convex hull of the player's eyes and the corners of the tile
tile1_fov_polygon = MultiPoint([
    (eyes_x, eyes_y),
    (tile1_x, tile1_y),
    (tile1_x + 1, tile1_y),
    (tile1_x + 1, tile1_y + 1),
    (tile1_x, tile1_y + 1),
]).convex_hull
print(tile1_fov_polygon)
# find all of the blocking boxes that intersect that convex hull polygon
print(blocking_box.intersects(tile1_fov_polygon))
# take the difference of the convex hull and the intersecting boxes
print(tile1_fov_polygon.difference(blocking_box))
# you know the tile is blocked as soon as you wind up with a multipolygon
print(type(tile1_fov_polygon.difference(blocking_box)))
# which is already the case here
print(
    'is_visible?',
    is_visible(Coordinates(player_x, player_y), Coordinates(tile1_x, tile1_y),
               [Coordinates(blocking_x, blocking_y)]))

print('\nTILE 2')
# another tile to check for visibility - should be visible
tile2_x, tile2_y = 5.0, 6.0
# take the convex hull of the player's eyes and the corners of the tile
tile2_fov_polygon = MultiPoint([
    (eyes_x, eyes_y),
    (tile2_x, tile2_y),
コード例 #6
0
    def draw(self, vsk: vsketch.Vsketch) -> None:
        print(os.getcwd())
        vsk.size("a6", landscape=False, center=False)
        vsk.scale(1)
        vsk.penWidth(self.pen_width)

        glyph_poly = load_glyph(self.font, self.glyph, self.face_index)

        # normalize glyph size
        bounds = glyph_poly.bounds
        scale_factor = min(
            (vsk.width - 2 * self.glyph_margin) / (bounds[2] - bounds[0]),
            (vsk.height - 2 * self.glyph_margin) / (bounds[3] - bounds[1]),
        )
        glyph_poly = scale(glyph_poly, scale_factor, scale_factor)
        bounds = glyph_poly.bounds
        glyph_poly = translate(
            glyph_poly,
            vsk.width / 2 - bounds[0] - (bounds[2] - bounds[0]) / 2,
            vsk.height / 2 - bounds[1] - (bounds[3] - bounds[1]) / 2 +
            self.glyph_voffset,
        )

        if self.draw_glyph:
            vsk.strokeWeight(self.glyph_weight)
            if self.fill_glyph:
                vsk.fill(1)
            vsk.geometry(glyph_poly)

            if self.fill_glyph and self.glyph_chroma:
                angle = self.glyph_chroma_angle / 180.0 * math.pi
                glyph_poly_chroma1 = translate(
                    glyph_poly,
                    -self.glyph_chroma_offset * math.cos(angle),
                    -self.glyph_chroma_offset * math.sin(angle),
                ).difference(glyph_poly)
                glyph_poly_chroma2 = translate(
                    glyph_poly,
                    self.glyph_chroma_offset * math.cos(angle),
                    self.glyph_chroma_offset * math.sin(angle),
                ).difference(glyph_poly)

                vsk.strokeWeight(1)
                vsk.stroke(2)
                vsk.fill(2)
                vsk.geometry(glyph_poly_chroma1)
                vsk.stroke(3)
                vsk.fill(3)
                vsk.geometry(glyph_poly_chroma2)

                glyph_poly = unary_union(
                    [glyph_poly, glyph_poly_chroma1, glyph_poly_chroma2])

            vsk.strokeWeight(1)
            vsk.stroke(1)
            vsk.noFill()

        glyph_shadow = None
        if self.glyph_shadow:
            angle = self.glyph_chroma_angle / 180.0 * math.pi
            glyph_shadow = translate(
                glyph_poly,
                self.glyph_chroma_offset * math.cos(angle),
                self.glyph_chroma_offset * math.sin(angle),
            ).difference(glyph_poly)
            vsk.fill(3)
            vsk.stroke(3)
            vsk.geometry(glyph_shadow)
            vsk.noFill()
            vsk.stroke(1)
            glyph_poly = glyph_poly.union(glyph_shadow)

        if self.glyph_weight == 1:
            glyph_poly_ext = glyph_poly.buffer(
                self.glyph_space,
                join_style=JOIN_STYLE.mitre,
            )
            glyph_poly_int = glyph_poly.buffer(
                -self.glyph_space_inside,
                join_style=JOIN_STYLE.mitre,
            )
        else:
            buf_len = (self.glyph_weight - 1) / 2 * self.pen_width
            glyph_poly_ext = glyph_poly.buffer(
                buf_len * 2 + self.glyph_space,
                join_style=JOIN_STYLE.mitre,
            )
            glyph_poly_int = glyph_poly.buffer(
                -buf_len - self.glyph_space_inside,
                join_style=JOIN_STYLE.mitre,
            )

        if glyph_shadow is not None:
            glyph_poly_int = glyph_poly_int.difference(glyph_shadow)

        # horizontal stripes
        if self.draw_h_stripes:
            count = round(
                (vsk.height - 2 * self.margin) / self.h_stripes_pitch)
            corrected_pitch = (vsk.height - 2 * self.margin) / count
            hstripes = MultiLineString([[
                (self.margin, self.margin + i * corrected_pitch),
                (vsk.width - self.margin, self.margin + i * corrected_pitch),
            ] for i in range(count + 1)])

            vsk.geometry(hstripes.difference(glyph_poly_ext))

            if self.h_stripes_inside:
                inside_stripes = translate(hstripes, 0, corrected_pitch /
                                           2).intersection(glyph_poly_int)
                vsk.geometry(inside_stripes)

                if self.h_stripes_inside_chroma:
                    chroma_offset = math.sqrt(2) * self.pen_width
                    vsk.stroke(2)
                    vsk.geometry(
                        translate(inside_stripes, -chroma_offset,
                                  -chroma_offset))
                    vsk.stroke(3)
                    vsk.geometry(
                        translate(inside_stripes, chroma_offset,
                                  chroma_offset))
                    vsk.stroke(1)

        # concentric
        if self.draw_concentric:
            circle_count = int(
                math.ceil(
                    math.hypot(vsk.width, vsk.height) / 2 /
                    self.concentric_pitch))
            circles = unary_union([
                Point(vsk.width / 2, vsk.height / 2).buffer(
                    (i + 1) * self.concentric_pitch,
                    resolution=int(1 * (i + 1) * self.concentric_pitch),
                ).exterior for i in range(circle_count)
            ])
            vsk.geometry(
                circles.difference(glyph_poly_ext).intersection(
                    box(
                        self.margin,
                        self.margin,
                        vsk.width - self.margin,
                        vsk.height - self.margin,
                    )))

        # dots
        vsk.fill(1)
        if self.draw_dots or self.draw_cut_circles:
            v_pitch = self.pitch * math.tan(math.pi / 3) / 2
            h_count = int((vsk.width - 2 * self.margin) // self.pitch)
            v_count = int((vsk.height - 2 * self.margin) // v_pitch)
            h_offset = (vsk.width - h_count * self.pitch) / 2
            v_offset = (vsk.height - v_count * v_pitch) / 2

            dot_array = []
            for j in range(v_count + 1):
                odd_line = j % 2 == 1
                for i in range(h_count + (0 if odd_line else 1)):
                    dot = Point(
                        h_offset + i * self.pitch +
                        (self.pitch / 2 if odd_line else 0),
                        v_offset + j * v_pitch,
                    ).buffer(self.thickness / 2)

                    if self.draw_dots:
                        if not dot.buffer(
                                self.thickness / 2).intersects(glyph_poly_ext):
                            dot_array.append(dot)
                    else:
                        dot_array.append(dot)

            dots = unary_union(dot_array)

            if self.draw_dots:
                vsk.geometry(dots)

            if self.draw_cut_circles:
                if self.cut_circles_inside:
                    op_func = lambda geom: geom.intersection(glyph_poly_int)
                else:
                    op_func = lambda geom: geom.difference(glyph_poly_ext)

                vsk.geometry(op_func(dots))

                if self.cut_circle_chroma:
                    angle = math.pi / 6
                    dist = self.pitch * 0.1
                    vsk.fill(2)
                    vsk.stroke(2)
                    vsk.geometry(
                        op_func(
                            translate(dots, -dist * math.cos(angle), -dist *
                                      math.sin(angle)).difference(dots)))
                    vsk.fill(3)
                    vsk.stroke(3)
                    vsk.geometry(
                        op_func(
                            translate(dots, dist * math.cos(angle),
                                      dist *
                                      math.sin(angle)).difference(dots)))
                    vsk.fill(1)
                    vsk.stroke(1)

        vsk.stroke(4)  # apply line sort, see finalize()
        if self.draw_dot_matrix:
            h_count = int(
                (vsk.width - 2 * self.margin) // self.dot_matrix_pitch) + 1
            v_count = int(
                (vsk.height - 2 * self.margin) // self.dot_matrix_pitch) + 1
            h_pitch = (vsk.width - 2 * self.margin) / (h_count - 1)
            v_pitch = (vsk.height - 2 * self.margin) / (v_count - 1)

            mp = MultiPoint([
                (self.margin + i * h_pitch, self.margin + j * v_pitch)
                for i, j in itertools.product(range(h_count), range(v_count))
                if vsk.random(1) < self.dot_matrix_density
            ])

            if self.draw_dot_matrix_inside:
                mp = mp.intersection(glyph_poly_int)
            else:
                mp = mp.difference(glyph_poly_ext)

            vsk.geometry(mp)
            vsk.vpype("color -l4 black")

        vsk.vpype("color -l1 black color -l2 cyan color -l3 magenta")
コード例 #7
0
ファイル: sid_afs_stats.py プロジェクト: loniitkina/sid
    
    #how to connect some more LKFs
    #calculate area of each polygon and select only the small ones 
    #calculate distance from centroid to all vertex, select top 10% vertex
    #make buffers around these selected vertexes, one by one
    #check if there is another polygon in that buffer (small or big)
    #if yes: connect the vertex to closest point in that buffer, draw a line, make buffer around that line, make that into a polygon, unify all 3 polygons

    
    #add a small frame (500m) along the edges of the region
    #this will close any floes that run accros the region edge
    frame = region.boundary.buffer(500)
    poly4 = poly_lkf.union(frame)

    #get difference of both = floes!
    floes = region.difference(poly4)
    
    #add holes (as floes)
    holes = unary_union(holes)
    floes = floes.union(holes)
    
    #save this polygons
    with open(outpath+'afs_poly_'+date+'_'+file_name_end, "wb") as poly_file:
        pickle.dump(floes, poly_file, pickle.HIGHEST_PROTOCOL)
        
    #simplify polygons
    #break to lines
    #calculate angles between them
    
    
    #Plotting
コード例 #8
0
def along_wall(
    polygons: Union[Polygon, MultiPolygon],
    walls: GeometryCollection,
    known_wall_points: np.ndarray,
    known_waypoints: np.ndarray,
    wall_pts_dist: float,
    inner_pts_dist: float,
    to_wall_dist: float,
    min_len: float,
    angle_support_dist: float,
    slack_lower: float,
    slack_upper: float,
    consider_wall_dist: float,
    wall_point_distance_multiplier: float,
    inner_point_distance_multiplier: float,
    generate_corner_waypoints: bool = True,
    plot: bool = False,
    min_item_count: int = 4,
    max_dist_to_consider_local: float = 40.0,
) -> Tuple[MultiPoint, MultiPoint, MultiPoint]:
  assert isinstance(polygons, (Polygon, MultiPolygon, GeometryCollection))
  if isinstance(polygons, GeometryCollection):
    polygons = [poly for poly in polygons if isinstance(poly, Polygon)]
    not_poly = [poly for poly in polygons if not isinstance(poly, Polygon)]
    assert all(item.length < 2.0
               for item in not_poly), f"{[type(item) for item in not_poly]}"
  if isinstance(polygons, Polygon):
    polygons = [polygons]

  known_waypoints_mp = MultiPoint(known_waypoints)
  known_wallpoints_mp = MultiPoint(known_wall_points)

  rings: List[LinearRing] = [
      interior for poly in polygons for interior in poly.interiors
  ]
  rings.extend(poly.exterior for poly in polygons)

  corner_points = []
  wall_points = []
  inner_points = []

  for r in rings:
    if r.length < min_len:
      continue

    if generate_corner_waypoints:
      corner_arr = locate_corners_linear(
          r, slack_lower=slack_lower, slack_upper=slack_upper)
      corner_mp = MultiPoint(corner_arr)
    else:
      corner_mp = MultiPoint(np.zeros(shape=(0, 2)))

    distance_to_wall = np.array([pt.distance(r) for pt in known_wallpoints_mp])
    wall_mask = distance_to_wall < consider_wall_dist
    current_wall_points = known_wall_points[wall_mask]
    current_known_wallpoints_mp = MultiPoint(current_wall_points)

    typical_inner_pt_dist = inner_pts_dist
    if len(current_known_wallpoints_mp) > 1:
      rest_kps = known_waypoints_mp.difference(current_known_wallpoints_mp)
      if (len(maybe_to_array(rest_kps)) >= min_item_count and
          len(maybe_to_array(current_known_wallpoints_mp)) > min_item_count):
        distances = nearest_dists(current_known_wallpoints_mp, rest_kps)
        distances = distances[(distances > 1.2) & (distances < 12.0)]
        if len(distances) > min_item_count:
          median_nearest_dist = np.median(distances)
          median_nearest_dist *= inner_point_distance_multiplier
          typical_inner_pt_dist = median_nearest_dist

      linear_dists = [r.project(pt) for pt in current_known_wallpoints_mp]
      linear_dists.append(r.length + min(linear_dists))
      linear_dists = np.array(linear_dists)
      sort_inds = np.argsort(linear_dists)
      wall_dists = [
          r.interpolate(wpd).distance(pt)
          for wpd, pt in zip(linear_dists, current_known_wallpoints_mp)
      ]
      wall_dists.append(wall_dists[0])
      wall_dists = np.array(wall_dists)
      linear_dists = linear_dists[sort_inds]
      wall_dists = wall_dists[sort_inds]
    else:
      linear_dists = np.array([0.0, r.length])
      wall_dists = np.array([to_wall_dist, to_wall_dist])

    pt_dists = np.diff(linear_dists)

    mask = (pt_dists > 1.2) & (pt_dists < 20.0)
    if len(pt_dists[mask]) < min_item_count:
      typical_wall_pt_dist = wall_pts_dist
    else:
      typical_wall_pt_dist = np.median(pt_dists[mask])

    gen_dist = wall_point_distance_multiplier * typical_wall_pt_dist
    gen_mask = pt_dists > 2 * gen_dist
    gen_inds = np.arange(len(linear_dists))[:-1][gen_mask]

    wn_sz = 4

    linear_corner_dists = [r.project(pt) for pt in corner_mp]
    corner_inds = np.searchsorted(linear_dists, linear_corner_dists)
    corner_to_wall_dists = []
    for i, ind in enumerate(corner_inds):
      local_dist_to_wall = to_wall_dist
      if len(wall_dists) > min_item_count:
        local_wall_dists = window_circular(wall_dists, ind - wn_sz,
                                           ind + 1 + wn_sz)
        local_linear_dists = window_circular(linear_dists, ind - wn_sz,
                                             ind + 1 + wn_sz)
        mask = (
            abs(local_linear_dists - linear_corner_dists[i]) <
            max_dist_to_consider_local)
        local_wall_dists = local_wall_dists[mask]
        if len(local_wall_dists) > min_item_count:
          local_dist_to_wall = np.median(local_wall_dists)

      corner_to_wall_dists.append(local_dist_to_wall)
    corner_to_wall_dists = np.array(corner_to_wall_dists)

    for start, w_dist in zip(linear_corner_dists, corner_to_wall_dists):
      corner_pts, inner_pts = wall_inner_outer_linear(
          line=r,
          walls=walls,
          start=start,
          inner_dist=typical_inner_pt_dist,
          angle_support_dist=angle_support_dist * 0.67,
          from_wall_dist=w_dist,
      )
      corner_points.extend(corner_pts)
      inner_points.extend(inner_pts)

    if len(gen_inds) > 0:
      gen_locs: List[np.ndarray] = []
      to_wall_dists: List[np.ndarray] = []

      for i in gen_inds:
        local_dist_to_wall = to_wall_dist
        if len(wall_dists) > min_item_count:
          local_wall_dists = window_circular(wall_dists, i - wn_sz,
                                             i + 1 + wn_sz)
          local_linear_dists = window_circular(linear_dists, i - wn_sz,
                                               i + 1 + wn_sz)
          mask = (abs(local_linear_dists - linear_dists[i]) <
                  max_dist_to_consider_local) | (
                      abs(local_linear_dists - linear_dists[i + 1]) <
                      max_dist_to_consider_local)
          local_wall_dists = local_wall_dists[mask]
          if len(local_wall_dists) > min_item_count:
            local_dist_to_wall = np.median(local_wall_dists)

        first = linear_dists[i]
        last = linear_dists[i + 1]
        open_dist = last - first
        n_points = round(open_dist / gen_dist)
        dist = open_dist / n_points
        tmp = np.arange(first + dist, last - dist / 2, dist)
        to_wall_dists.append(
            np.full(shape=tmp.shape, fill_value=local_dist_to_wall))
        gen_locs.append(tmp)
      to_wall_dists = np.concatenate(to_wall_dists)
      gen_locs = np.concatenate(gen_locs)

      if plot:
        plot_polygon(Polygon(r), data_1=to_array(known_waypoints_mp))
        plot_polygon(Polygon(r), data_1=to_array(known_wallpoints_mp))
        plot_polygon(Polygon(r), data_1=to_array(current_known_wallpoints_mp))
        plot_polygon(
            Polygon(r),
            data_1=to_array(MultiPoint([r.interpolate(l) for l in gen_locs])),
        )
        plot_polygon(
            Polygon(r),
            data_1=to_array(MultiPoint([r.interpolate(l) for l in gen_locs])),
            data_2=to_array(
                MultiPoint([r.interpolate(l) for l in linear_dists])),
        )
      gen_locs[gen_locs > r.length] -= r.length
      for start, w_dist in zip(gen_locs, to_wall_dists):
        if (not corner_mp.is_empty and
            r.interpolate(start).distance(corner_mp) < gen_dist):
          continue
        wall_pts, inner_pts = wall_inner_outer_linear(
            line=r,
            walls=walls,
            start=start,
            inner_dist=typical_inner_pt_dist,
            angle_support_dist=angle_support_dist,
            from_wall_dist=w_dist,
        )
        wall_points.extend(wall_pts)
        inner_points.extend(inner_pts)

  corner_points = MultiPoint(corner_points)
  inner_points = MultiPoint(inner_points)
  wall_points = MultiPoint(wall_points)
  return corner_points, wall_points, inner_points
コード例 #9
0
def near_wall_stats(
    polygons: Union[Polygon, MultiPolygon],
    known_wall_points: np.ndarray,
    known_waypoints: np.ndarray,
    min_len: float,
    maybe_wall_dist: float,
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
  assert isinstance(polygons, (Polygon, MultiPolygon, GeometryCollection, list))
  if isinstance(polygons, (GeometryCollection, list)):
    polygons = [poly for poly in polygons if isinstance(poly, Polygon)]
    not_poly = [poly for poly in polygons if not isinstance(poly, Polygon)]
    assert all(item.length < 2.0
               for item in not_poly), f"{[type(item) for item in not_poly]}"
  if isinstance(polygons, Polygon):
    polygons = [polygons]

  known_waypoints_mp = MultiPoint(known_waypoints)
  known_wallpoints_mp = MultiPoint(known_wall_points)

  rings: List[LinearRing] = [
      interior for poly in polygons for interior in poly.interiors
  ]
  rings.extend(poly.exterior for poly in polygons)

  to_wall_distances = []
  along_wall_distances = []
  near_wall_to_rest_distances = []

  for r in rings:
    if r.length < min_len:
      continue

    distance_to_wall = np.array([pt.distance(r) for pt in known_wallpoints_mp])
    wall_mask = distance_to_wall < maybe_wall_dist
    current_wall_points = known_wall_points[wall_mask]
    current_known_wallpoints_mp = MultiPoint(current_wall_points)

    if len(current_known_wallpoints_mp) <= 1:
      continue

    rest_kps = known_waypoints_mp.difference(current_known_wallpoints_mp)

    if (len(maybe_to_array(current_known_wallpoints_mp)) == 0 or
        len(maybe_to_array(rest_kps)) == 0):
      near_dists = np.zeros(shape=(0, 2))
    else:
      near_dists = nearest_dists(current_known_wallpoints_mp, rest_kps)

    linear_dists = [r.project(pt) for pt in current_known_wallpoints_mp]
    linear_dists.append(r.length + min(linear_dists))
    linear_dists = np.array(linear_dists)
    sort_inds = np.argsort(linear_dists)
    to_wall_dists = [
        r.interpolate(wpd).distance(pt)
        for wpd, pt in zip(linear_dists, current_known_wallpoints_mp)
    ]
    to_wall_dists.append(to_wall_dists[0])
    to_wall_dists = np.array(to_wall_dists)
    linear_dists = linear_dists[sort_inds]
    to_wall_dists = to_wall_dists[sort_inds]
    wall_pts_dists = np.diff(linear_dists)

    to_wall_distances.append(to_wall_dists)
    along_wall_distances.append(wall_pts_dists)
    near_wall_to_rest_distances.append(near_dists)

  to_wall_distances = maybe_concat(to_wall_distances)
  along_wall_distances = maybe_concat(along_wall_distances)
  near_wall_to_rest_distances = maybe_concat(near_wall_to_rest_distances)

  return to_wall_distances, along_wall_distances, near_wall_to_rest_distances