void SymbolBucket::addFeature(const std::vector<std::vector<Coordinate>> &lines,
        const Shaping &shapedText, const PositionedIcon &shapedIcon, const GlyphPositions &face) {

    const float minScale = 0.5f;
    const float glyphSize = 24.0f;

    const float fontScale = layout.text.size / glyphSize;
    const float textBoxScale = tilePixelRatio * fontScale;
    const float textMaxBoxScale = tilePixelRatio * layout.textMaxSize / glyphSize;
    const float iconBoxScale = tilePixelRatio * layout.icon.size;
    const float symbolSpacing = tilePixelRatio * layout.spacing;
    const bool avoidEdges = layout.avoidEdges && layout.placement != PlacementType::Line;
    const float textPadding = layout.text.padding * tilePixelRatio;
    const float iconPadding = layout.icon.padding * tilePixelRatio;
    const float textMaxAngle = layout.text.maxAngle * M_PI / 180;
    const bool textAlongLine =
        layout.text.rotationAlignment == RotationAlignmentType::Map &&
        layout.placement == PlacementType::Line;
    const bool iconAlongLine =
        layout.icon.rotationAlignment == RotationAlignmentType::Map &&
        layout.placement == PlacementType::Line;
    const bool mayOverlap = layout.text.allowOverlap || layout.icon.allowOverlap ||
        layout.text.ignorePlacement || layout.icon.ignorePlacement;
    const bool isLine = layout.placement == PlacementType::Line;
    const float textRepeatDistance = symbolSpacing / 2;

    auto& clippedLines = isLine ?
        util::clipLines(lines, 0, 0, 4096, 4096) :
        lines;

    for (const auto& line : clippedLines) {
        if (line.empty()) continue;

        // Calculate the anchor points around which you want to place labels
        Anchors anchors = isLine ?
            getAnchors(line, symbolSpacing, textMaxAngle, shapedText.left, shapedText.right, shapedIcon.left, shapedIcon.right, glyphSize, textMaxBoxScale, overscaling) :
            Anchors({ Anchor(float(line[0].x), float(line[0].y), 0, minScale) });

        // For each potential label, create the placement features used to check for collisions, and the quads use for rendering.
        for (Anchor &anchor : anchors) {
            if (shapedText && isLine) {
                if (anchorIsTooClose(shapedText.text, textRepeatDistance, anchor)) {
                    continue;
                }
            }

            const bool inside = !(anchor.x < 0 || anchor.x > 4096 || anchor.y < 0 || anchor.y > 4096);

            if (avoidEdges && !inside) continue;

            // Normally symbol layers are drawn across tile boundaries. Only symbols
            // with their anchors within the tile boundaries are added to the buffers
            // to prevent symbols from being drawn twice.
            //
            // Symbols in layers with overlap are sorted in the y direction so that
            // symbols lower on the canvas are drawn on top of symbols near the top.
            // To preserve this order across tile boundaries these symbols can't
            // be drawn across tile boundaries. Instead they need to be included in
            // the buffers for both tiles and clipped to tile boundaries at draw time.
            //
            // TODO remove the `&& false` when is #1673 implemented
            const bool addToBuffers = (mode == MapMode::Still) || inside || (mayOverlap && false);

            symbolInstances.emplace_back(anchor, line, shapedText, shapedIcon, layout, addToBuffers,
                    textBoxScale, textPadding, textAlongLine,
                    iconBoxScale, iconPadding, iconAlongLine,
                    face);
        }
    }
}
void SymbolLayout::addFeature(const std::size_t index,
                              const SymbolFeature& feature,
                              const std::pair<Shaping, Shaping>& shapedTextOrientations,
                              optional<PositionedIcon> shapedIcon,
                              const GlyphPositionMap& glyphPositionMap,
                              const OverscaledTileID& tileID,
                              const std::string& sourceID) {
    const float minScale = 0.5f;
    const float glyphSize = 24.0f;
    
    const float layoutTextSize = layout.evaluate<TextSize>(zoom + 1, feature);
    const float layoutIconSize = layout.evaluate<IconSize>(zoom + 1, feature);
    const std::array<float, 2> textOffset = layout.evaluate<TextOffset>(zoom, feature);
    const std::array<float, 2> iconOffset = layout.evaluate<IconOffset>(zoom, feature);
    
    // To reduce the number of labels that jump around when zooming we need
    // to use a text-size value that is the same for all zoom levels.
    // This calculates text-size at a high zoom level so that all tiles can
    // use the same value when calculating anchor positions.
    const float textMaxSize = layout.evaluate<TextSize>(18, feature);
    
    const float fontScale = layoutTextSize / glyphSize;
    const float textBoxScale = tilePixelRatio * fontScale;
    const float textMaxBoxScale = tilePixelRatio * textMaxSize / glyphSize;
    const float iconBoxScale = tilePixelRatio * layoutIconSize;
    const float symbolSpacing = tilePixelRatio * layout.get<SymbolSpacing>();
    const bool avoidEdges = layout.get<SymbolAvoidEdges>() && layout.get<SymbolPlacement>() != SymbolPlacementType::Line;
    const float textPadding = layout.get<TextPadding>() * tilePixelRatio;
    const float iconPadding = layout.get<IconPadding>() * tilePixelRatio;
    const float textMaxAngle = layout.get<TextMaxAngle>() * util::DEG2RAD;
    const SymbolPlacementType textPlacement = layout.get<TextRotationAlignment>() != AlignmentType::Map
                                                  ? SymbolPlacementType::Point
                                                  : layout.get<SymbolPlacement>();

    const float textRepeatDistance = symbolSpacing / 2;
    IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketName, symbolInstances.size(),
                                     sourceID, tileID.canonical);

    auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) {
        // https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers
        // +-------------------+ Symbols with anchors located on tile edges
        // |(0,0)             || are duplicated on neighbor tiles.
        // |                  ||
        // |                  || In continuous mode, to avoid overdraw we
        // |                  || skip symbols located on the extent edges.
        // |       Tile       || In still mode, we include the features in
        // |                  || the buffers for both tiles and clip them
        // |                  || at draw time.
        // |                  ||
        // +-------------------| In this scenario, the inner bounding box
        // +-------------------+ is called 'withinPlus0', and the outer
        //       (extent,extent) is called 'inside'.
        const bool withinPlus0 = anchor.point.x >= 0 && anchor.point.x < util::EXTENT && anchor.point.y >= 0 && anchor.point.y < util::EXTENT;
        const bool inside = withinPlus0 || anchor.point.x == util::EXTENT || anchor.point.y == util::EXTENT;

        if (avoidEdges && !inside) return;

        if (mode == MapMode::Tile || withinPlus0) {
            symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon,
                    layout.evaluate(zoom, feature), layoutTextSize,
                    symbolInstances.size(),
                    textBoxScale, textPadding, textPlacement, textOffset,
                    iconBoxScale, iconPadding, iconOffset,
                    glyphPositionMap, indexedFeature, index, feature.text.value_or(std::u16string()), overscaling);
        }
    };
    
    const auto& type = feature.getType();

    if (layout.get<SymbolPlacement>() == SymbolPlacementType::Line) {
        auto clippedLines = util::clipLines(feature.geometry, 0, 0, util::EXTENT, util::EXTENT);
        for (const auto& line : clippedLines) {
            Anchors anchors = getAnchors(line,
                                         symbolSpacing,
                                         textMaxAngle,
                                         (shapedTextOrientations.second ?: shapedTextOrientations.first).left,
                                         (shapedTextOrientations.second ?: shapedTextOrientations.first).right,
                                         (shapedIcon ? shapedIcon->left() : 0),
                                         (shapedIcon ? shapedIcon->right() : 0),
                                         glyphSize,
                                         textMaxBoxScale,
                                         overscaling);

            for (auto& anchor : anchors) {
                if (!feature.text || !anchorIsTooClose(*feature.text, textRepeatDistance, anchor)) {
                    addSymbolInstance(line, anchor);
                }
            }
        }
    } else if (type == FeatureType::Polygon) {