bool placement_finder::find_line_placements(T & path, bool points)
{
    if (!layouts_.line_count()) return true; //TODO
    vertex_cache pp(path);

    bool success = false;
    while (pp.next_subpath())
    {
        if (points)
        {
            if (pp.length() <= 0.001)
            {
                success = find_point_placement(pp.current_position()) || success;
                continue;
            }
        }
        else
        {
            if ((pp.length() < text_props_->minimum_path_length * scale_factor_)
                ||
                (pp.length() <= 0.001) // Clipping removed whole geometry
                ||
                (pp.length() < layouts_.width()))
                {
                    continue;
                }
        }

        double spacing = get_spacing(pp.length(), points ? 0. : layouts_.width());

        //horizontal_alignment_e halign = layouts_.back()->horizontal_alignment();

        // halign == H_LEFT -> don't move
        if (horizontal_alignment_ == H_MIDDLE || horizontal_alignment_ == H_AUTO || horizontal_alignment_ == H_ADJUST)
        {
            if (!pp.forward(spacing / 2.0)) continue;
        }
        else if (horizontal_alignment_ == H_RIGHT)
        {
            if (!pp.forward(pp.length())) continue;
        }

        if (move_dx_ != 0.0) path_move_dx(pp, move_dx_);

        do
        {
            tolerance_iterator tolerance_offset(text_props_->label_position_tolerance * scale_factor_, spacing); //TODO: Handle halign
            while (tolerance_offset.next())
            {
                vertex_cache::scoped_state state(pp);
                if (pp.move(tolerance_offset.get())
                    && ((points && find_point_placement(pp.current_position()))
                        || (!points && single_line_placement(pp, text_props_->upright))))
                {
                    success = true;
                    break;
                }
            }
        } while (pp.forward(spacing));
    }
    return success;
}
예제 #2
0
bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e orientation)
{
    //
    // IMPORTANT NOTE: See note about coordinate systems in find_point_placement()!
    //

    vertex_cache::scoped_state begin(pp);
    text_upright_e real_orientation = simplify_upright(orientation, pp.angle());

    glyph_positions_ptr glyphs = std::make_shared<glyph_positions>();
    std::vector<box2d<double> > bboxes;
    glyphs->reserve(layouts_.glyphs_count());
    bboxes.reserve(layouts_.glyphs_count());

    unsigned upside_down_glyph_count = 0;

    for (auto const& layout_ptr : layouts_)
    {
        text_layout const& layout = *layout_ptr;
        pixel_position align_offset = layout.alignment_offset();
        pixel_position const& layout_displacement = layout.displacement();
        double sign = (real_orientation == UPRIGHT_LEFT) ? -1 : 1;
        double offset = layout_displacement.y + 0.5 * sign * layout.height();

        for (auto const& line : layout)
        {
            // Only subtract half the line height here and half at the end because text is automatically
            // centered on the line
            offset -= sign * line.height()/2;
            vertex_cache & off_pp = pp.get_offseted(offset, sign*layout.width());
            vertex_cache::scoped_state off_state(off_pp); // TODO: Remove this when a clean implementation in vertex_cache::get_offseted is done

            if (!off_pp.move(sign * layout.jalign_offset(line.width()) - align_offset.x)) return false;

            double last_cluster_angle = 999;
            int current_cluster = -1;
            pixel_position cluster_offset;
            double angle;
            rotation rot;
            double last_glyph_spacing = 0.;

            for (auto const& glyph : line)
            {
                if (current_cluster != static_cast<int>(glyph.char_index))
                {
                    if (!off_pp.move_to_distance(sign * (layout.cluster_width(current_cluster) + last_glyph_spacing)))
                        return false;

                    current_cluster = glyph.char_index;
                    last_glyph_spacing = glyph.format->character_spacing * scale_factor_;
                    // Only calculate new angle at the start of each cluster!
                    angle = normalize_angle(off_pp.angle(sign * layout.cluster_width(current_cluster)));
                    rot.init(angle);
                    if ((text_props_->max_char_angle_delta > 0) && (last_cluster_angle != 999) &&
                        std::fabs(normalize_angle(angle-last_cluster_angle)) > text_props_->max_char_angle_delta)
                    {
                        return false;
                    }
                    cluster_offset.clear();
                    last_cluster_angle = angle;
                }

                if (std::abs(angle) > M_PI/2) ++upside_down_glyph_count;

                pixel_position pos = off_pp.current_position() + cluster_offset;
                // Center the text on the line
                double char_height = line.max_char_height();
                pos.y = -pos.y - char_height/2.0*rot.cos;
                pos.x =  pos.x + char_height/2.0*rot.sin;

                cluster_offset.x += rot.cos * glyph.advance();
                cluster_offset.y -= rot.sin * glyph.advance();

                box2d<double> bbox = get_bbox(layout, glyph, pos, rot);
                if (collision(bbox, layouts_.text(), true)) return false;
                bboxes.push_back(std::move(bbox));
                glyphs->emplace_back(glyph, pos, rot);
            }
            // See comment above
            offset -= sign * line.height()/2;
        }
    }

    if (upside_down_glyph_count > static_cast<unsigned>(layouts_.text().length() / 2))
    {
        if (orientation == UPRIGHT_AUTO)
        {
            // Try again with opposite orientation
            begin.restore();
            return single_line_placement(pp, real_orientation == UPRIGHT_RIGHT ? UPRIGHT_LEFT : UPRIGHT_RIGHT);
        }
        // upright==left_only or right_only and more than 50% of characters upside down => no placement
        else if (orientation == UPRIGHT_LEFT_ONLY || orientation == UPRIGHT_RIGHT_ONLY)
        {
            return false;
        }
    }

    for (box2d<double> const& box : bboxes)
    {
        detector_.insert(box, layouts_.text());
    }
    placements_.push_back(glyphs);

    return true;
}
예제 #3
0
bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e orientation)
{
    //
    // IMPORTANT NOTE: See note about coordinate systems in find_point_placement()!
    //

    vertex_cache::scoped_state begin(pp);
    text_upright_e real_orientation = simplify_upright(orientation, pp.angle());

    glyph_positions_ptr glyphs = std::make_shared<glyph_positions>();
    std::vector<box2d<double> > bboxes_all;
    glyphs->reserve(layouts_.glyphs_count());
    bboxes_all.reserve(layouts_.glyphs_count());

    unsigned upside_down_glyph_count = 0;
    marker_placement_finder_ptr marker_placement_ptr;
    for (auto const& layout_ptr : layouts_)
    {
        text_layout const& layout = *layout_ptr;

        if(has_marker_ && layout.shield_layout())
        {
            marker_placement_ptr = std::make_shared<marker_placement_finder>(layout_ptr);
        }
        pixel_position align_offset = layout.alignment_offset();
        pixel_position const& layout_displacement = layout.displacement();
        double sign = (real_orientation == UPRIGHT_LEFT) ? -1 : 1;
        double offset = layout_displacement.y + 0.5 * sign * layout.height();
        double adjust_character_spacing = .0;
        double layout_width = layout.width();
        bool adjust = layout.horizontal_alignment() == H_ADJUST;

        if (adjust)
        {
            text_layout::const_iterator longest_line = layout.longest_line();
            if (longest_line != layout.end())
            {
                adjust_character_spacing = (pp.length() - longest_line->glyphs_width()) / longest_line->space_count();
                layout_width = longest_line->glyphs_width() + longest_line->space_count() * adjust_character_spacing;
            }
        }
        //find the proper center line
        int line_count = layout.num_lines() % 2 == 0 ? layout.num_lines() : layout.num_lines() - 1;
        int centre_line_index = line_count/2;
        int line_index = -1;
        for (auto const& line : layout)
        {
            line_index++;
            if(has_marker_ && marker_placement_ptr &&
                    (line_index == centre_line_index))
            {
                marker_placement_ptr->line_size(line.size());
            }
            // Only subtract half the line height here and half at the end because text is automatically
            // centered on the line
            if (layout.num_lines() == 1 || (layout.num_lines() > 1 && line_index != 0))
            {
                offset -= sign * line.height()/2;
            }

            vertex_cache & off_pp = pp.get_offseted(offset, sign * layout_width);
            vertex_cache::scoped_state off_state(off_pp); // TODO: Remove this when a clean implementation in vertex_cache::get_offseted is done
            double line_width = adjust ? (line.glyphs_width() + line.space_count() * adjust_character_spacing) : line.width();

            if (!off_pp.move(sign * layout.jalign_offset(line_width) - align_offset.x)) return false;

            double last_cluster_angle = 999;
            int current_cluster = -1;
            pixel_position cluster_offset;
            double angle = 0.0;
            rotation rot;
            double last_glyph_spacing = 0.;

            for (auto const& glyph : line)
            {
                if (current_cluster != static_cast<int>(glyph.char_index))
                {
                    if (adjust)
                    {
                        if (!off_pp.move(sign * (layout.cluster_width(current_cluster) + last_glyph_spacing)))
                            return false;
                        last_glyph_spacing = adjust_character_spacing;
                    }
                    else
                    {
                        if (!off_pp.move_to_distance(sign * (layout.cluster_width(current_cluster) + last_glyph_spacing)))
                            return false;
                        last_glyph_spacing = glyph.format->character_spacing * scale_factor_;
                    }
                    current_cluster = glyph.char_index;
                    // Only calculate new angle at the start of each cluster!
                    angle = normalize_angle(off_pp.angle(sign * layout.cluster_width(current_cluster)));
                    rot.init(angle);
                    if ((text_props_->max_char_angle_delta > 0) && (last_cluster_angle != 999) &&
                            std::fabs(normalize_angle(angle-last_cluster_angle)) > text_props_->max_char_angle_delta)
                    {
                        return false;
                    }
                    cluster_offset.clear();
                    last_cluster_angle = angle;
                }
                if (std::abs(angle) > M_PI/2) ++upside_down_glyph_count;

                pixel_position pos = off_pp.current_position() + cluster_offset;
                // Center the text on the line
                double char_height = line.max_char_height();
                pos.y = -pos.y - char_height/2.0*rot.cos;
                pos.x =  pos.x + char_height/2.0*rot.sin;

                cluster_offset.x += rot.cos * glyph.advance();
                cluster_offset.y -= rot.sin * glyph.advance();

                box2d<double> bbox = get_bbox(layout, glyph, pos, rot);
                if (collision(bbox, layouts_.text(), true)) return false;

                //Whitespace glyphs have 0 height - sadly, there is no other easily
                //  accessible data in a glyph to indicate that it is whitespace.
                bool glyph_is_empty = (0 == glyph.height());

                //Collect all glyphs and angles for the centre line, in the case a
                //  marker position needs to be calculated later
                if(has_marker_ && marker_placement_ptr
                        && layout.shield_layout()
                        && (line_index == centre_line_index))
                {
                    marker_placement_ptr->add_angle(angle);
                    marker_placement_ptr->add_bbox(bbox);
                }

                //If the glyph is empty, don't draw it, and don't include it in the
                //  overall bounding box. This means that leading and trailing
                //  whitespace characters don't take up extra space where other
                //  symbols could be placed on a map.
                if(!glyph_is_empty)
                {
                    bboxes_all.push_back(std::move(bbox));
                    glyphs->emplace_back(glyph, pos, rot);
                }
            }
            // See comment above
            offset -= sign * line.height()/2;
        }
        if(marker_placement_ptr)
        {
            marker_placement_ptr->align_offset(align_offset);
        }
    }
    if (upside_down_glyph_count > static_cast<unsigned>(layouts_.text().length() / 2))
    {
        if (orientation == UPRIGHT_AUTO)
        {
            // Try again with opposite orientation
            begin.restore();
            return single_line_placement(pp, real_orientation == UPRIGHT_RIGHT ? UPRIGHT_LEFT : UPRIGHT_RIGHT);
        }
        // upright==left_only or right_only and more than 50% of characters upside down => no placement
        else if (orientation == UPRIGHT_LEFT_ONLY || orientation == UPRIGHT_RIGHT_ONLY)
        {
            return false;
        }
    }

    //NOTE: Because of the glyph_is_empty check above, whitespace characters
    //      don't factor in to the bounding box. This way, whitespace used for
    //      layout adjustment purposes don't artificially increase the size of
    //      the bounding box and reduce the density of possible mapnik symbols.
    box2d<double> overall_bbox;
    bool overall_bbox_initialized = false;
    for(box2d<double> const& bbox : bboxes_all)
    {
        detector_.insert(bbox, layouts_.text());
        if(overall_bbox_initialized)
        {
            overall_bbox.expand_to_include(bbox);
        }
        else
        {
            overall_bbox = bbox;
        }
    }

    //WI: loop over marker_placements and place the markers for each layout
    if(has_marker_ && marker_placement_ptr )
    {
        pixel_position marker_pos;
        double marker_cw_angle = 0.0;
        marker_placement_ptr->find_marker_placement(scale_factor_, marker_pos, marker_cw_angle);
        agg::trans_affine placement_tr;
        placement_tr.rotate( marker_cw_angle );
        add_marker(glyphs, marker_pos, true, placement_tr);
        detector_.insert(overall_bbox, layouts_.text());
    }
    placements_.push_back(glyphs);
    return true;
}