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; }
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; }
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; }