pixel_position evaluate_displacement(double dx, double dy, directions_e dir) { switch (dir) { case EXACT_POSITION: return pixel_position(dx,dy); break; case NORTH: return pixel_position(0,-std::abs(dy)); break; case EAST: return pixel_position(std::abs(dx),0); break; case SOUTH: return pixel_position(0,std::abs(dy)); break; case WEST: return pixel_position(-std::abs(dx),0); break; case NORTHEAST: return pixel_position(std::abs(dx),-std::abs(dy)); break; case SOUTHEAST: return pixel_position(std::abs(dx),std::abs(dy)); break; case NORTHWEST: return pixel_position(-std::abs(dx),-std::abs(dy)); break; case SOUTHWEST: return pixel_position(-std::abs(dx),std::abs(dy)); break; } }
typename const_accessort_t::data_t integrate(const const_accessort_t & image) const { typename const_accessort_t::data_t result = 0.0; ublas::fixed_vector<float_t, SHAPE_DIMENSION> point, normal; for(size_t i = 0; i < n_points(); ++i) { point = (*this)(i, normal); // explicit cast of floating point values in "vertex" to pixel position! ublas::fixed_vector<size_t, const_accessort_t::dimension> pixel_position = ublas::fixed_vector<size_t, const_accessort_t::dimension>(point); bool is_valid_pixel = true; for(size_t i = 0; i < const_accessort_t::dimension; ++i) { if( ! ( pixel_position(i) < image.size()(i) ) ) { is_valid_pixel = false; break; } } if(is_valid_pixel) result += image[pixel_position] * norm_2(normal); } return result; }
box2d<double> placement_finder::get_bbox(text_layout const& layout, glyph_info const& glyph, pixel_position const& pos, rotation const& rot) { /* (0/ymax) (width/ymax) *************** * * (0/0)* * * * *************** (0/ymin) (width/ymin) Add glyph offset in y direction, but not in x direction (as we use the full cluster width anyways)! */ double width = layout.cluster_width(glyph.char_index); if (glyph.advance() <= 0) width = -width; pixel_position tmp, tmp2; tmp.set(0, glyph.ymax()); tmp = tmp.rotate(rot); tmp2.set(width, glyph.ymax()); tmp2 = tmp2.rotate(rot); box2d<double> bbox(tmp.x, -tmp.y, tmp2.x, -tmp2.y); tmp.set(width, glyph.ymin()); tmp = tmp.rotate(rot); bbox.expand_to_include(tmp.x, -tmp.y); tmp.set(0, glyph.ymin()); tmp = tmp.rotate(rot); bbox.expand_to_include(tmp.x, -tmp.y); pixel_position pos2 = pos + pixel_position(0, glyph.offset.y).rotate(rot); bbox.move(pos2.x , -pos2.y); return bbox; }
// arrange group members in x horizontal pairs of 2, // one to the left and one to the right of center in each pair void operator()(pair_layout const& layout) const { member_offsets_.resize(member_boxes_.size()); double y_margin = layout.get_item_margin(); double x_margin = y_margin / 2.0; if (member_boxes_.size() == 1) { member_offsets_[0] = pixel_position(0, 0) - input_origin_; return; } auto max_diff = layout.get_max_difference(); auto layout_box = make_horiz_pair(0, 0.0, 0, x_margin, max_diff); auto y_shift = 0.5 * layout_box.height(); for (size_t i = 2; i < member_boxes_.size(); i += 2) { auto y = layout_box.maxy() + y_margin; auto pair_box = make_horiz_pair(i, y, 1, x_margin, max_diff); layout_box.expand_to_include(pair_box); } // layout_box.center corresponds to the center of the first row; // shift offsets so that the whole group is centered vertically y_shift -= 0.5 * layout_box.height(); for (auto & offset : member_offsets_) { offset.y += y_shift; } }
pixel_position shield_symbolizer_helper<FaceManagerT, DetectorT>::get_marker_position(text_path const& p) { position const& pos = placement_->properties.displacement; if (placement_->properties.label_placement == LINE_PLACEMENT) { double lx = p.center.x - pos.first; double ly = p.center.y - pos.second; double px = lx - 0.5*marker_w_; double py = ly - 0.5*marker_h_; marker_ext_.re_center(lx, ly); //label is added to detector by get_line_placement(), but marker isn't detector_.insert(marker_ext_); return pixel_position(px, py); } else { //collision_detector is already updated for point placement in get_point_placement() return pixel_position(marker_x_, marker_y_); } }
void add_path(T & path) { marker_placement_enum placement_method = get<marker_placement_enum>( sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT); bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false); bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false); double opacity = get<double>(sym_, keys::opacity, feature_, vars_, 1.0); double spacing = get<double>(sym_, keys::spacing, feature_, vars_, 100.0); double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2); coord2d center = bbox_.center(); agg::trans_affine_translation recenter(-center.x, -center.y); agg::trans_affine tr = recenter * marker_trans_; markers_placement_finder<T, label_collision_detector4> placement_finder( placement_method, path, bbox_, tr, detector_, spacing * scale_factor_, max_error, allow_overlap); double x, y, angle = .0; while (placement_finder.get_point(x, y, angle, ignore_placement)) { agg::trans_affine matrix = tr; matrix.rotate(angle); matrix.translate(x, y); render_vector_marker( ctx_, pixel_position(x, y), marker_, bbox_, attributes_, matrix, opacity, false); } }
float_t integrate_vector_field (const const_vector_accessor_t & vector_field) const { float_t result = 0.0; ublas::fixed_vector<float_t, SHAPE_DIMENSION> point, normal; for(size_t i = 0; i < n_points(); ++i) { point = (*this)(i, normal); // explicit cast of floating point values in "vertex" to pixel position! ublas::fixed_vector<size_t, const_vector_accessor_t::dimension> pixel_position = ublas::fixed_vector<size_t, const_vector_accessor_t::dimension>(point); bool is_valid_pixel = true; for(size_t i = 0; i < const_vector_accessor_t::dimension; ++i) { if( ! ( pixel_position(i) < vector_field.size()(i) ) ) { is_valid_pixel = false; break; } } if(is_valid_pixel) // Note: unit normal * tangential speed = normal result += inner_prod(vector_field[pixel_position], normal); else { ublas::fixed_vector<float_t, SHAPE_DIMENSION> value; boundary_discretizer_impl::compute_zero_extension(vector_field, point, value); result += inner_prod(value, normal); } } return result; }
bool placement_finder::find_point_placement(pixel_position const& pos) { glyph_positions_ptr glyphs = std::make_shared<glyph_positions>(); std::vector<box2d<double> > bboxes; glyphs->reserve(layouts_.glyphs_count()); bboxes.reserve(layouts_.size()); bool base_point_set = false; for (auto const& layout_ptr : layouts_) { text_layout const& layout = *layout_ptr; rotation const& orientation = layout.orientation(); // Find text origin. pixel_position layout_center = pos + layout.displacement(); if (!base_point_set) { glyphs->set_base_point(layout_center); base_point_set = true; } box2d<double> bbox = layout.bounds(); bbox.re_center(layout_center.x, layout_center.y); /* For point placements it is faster to just check the bounding box. */ if (collision(bbox, layouts_.text(), false)) return false; if (layout.num_lines()) bboxes.push_back(std::move(bbox)); pixel_position layout_offset = layout_center - glyphs->get_base_point(); layout_offset.y = -layout_offset.y; // IMPORTANT NOTE: // x and y are relative to the center of the text // coordinate system: // x: grows from left to right // y: grows from bottom to top (opposite of normal computer graphics) double x, y; // set for upper left corner of text envelope for the first line, top left of first character y = layout.height() / 2.0; for ( auto const& line : layout) { y -= line.height(); //Automatically handles first line differently x = layout.jalign_offset(line.width()); for (auto const& glyph : line) { // place the character relative to the center of the string envelope glyphs->emplace_back(glyph, (pixel_position(x, y).rotate(orientation)) + layout_offset, orientation); if (glyph.advance()) { //Only advance if glyph is not part of a multiple glyph sequence x += glyph.advance() + glyph.format->character_spacing * scale_factor_; } } } } // add_marker first checks for collision and then updates the detector. if (has_marker_ && !add_marker(glyphs, pos)) return false; for (box2d<double> const& bbox : bboxes) { detector_.insert(bbox, layouts_.text()); } placements_.push_back(glyphs); return true; }
void base_symbolizer_helper::initialize_points() { label_placement_enum how_placed = placement_->properties.label_placement; if (how_placed == LINE_PLACEMENT) { point_placement_ = false; return; } else { point_placement_ = true; } double label_x=0.0; double label_y=0.0; double z=0.0; for (auto * geom_ptr : geometries_to_process_) { geometry_type const& geom = *geom_ptr; if (how_placed == VERTEX_PLACEMENT) { geom.rewind(0); for(unsigned i = 0; i < geom.size(); ++i) { geom.vertex(&label_x, &label_y); prj_trans_.backward(label_x, label_y, z); t_.forward(&label_x, &label_y); points_.push_back(pixel_position(label_x, label_y)); } } else { // https://github.com/mapnik/mapnik/issues/1423 bool success = false; // https://github.com/mapnik/mapnik/issues/1350 if (geom.type() == geometry_type::types::LineString) { success = label::middle_point(geom, label_x,label_y); } else if (how_placed == POINT_PLACEMENT) { success = label::centroid(geom, label_x, label_y); } else if (how_placed == INTERIOR_PLACEMENT) { success = label::interior_position(geom, label_x, label_y); } else { MAPNIK_LOG_ERROR(symbolizer_helpers) << "ERROR: Unknown placement type in initialize_points()"; } if (success) { prj_trans_.backward(label_x, label_y, z); t_.forward(&label_x, &label_y); points_.push_back(pixel_position(label_x, label_y)); } } } point_itr_ = points_.begin(); }
void cairo_context::add_text(glyph_positions const& pos, cairo_face_manager & manager, composite_mode_e comp_op, composite_mode_e halo_comp_op, double scale_factor) { pixel_position const& base_point = pos.get_base_point(); const double sx = base_point.x; const double sy = base_point.y; for (auto const& glyph_pos : pos) { glyph_info const& glyph = glyph_pos.glyph; glyph.face->set_character_sizes(glyph.format->text_size * scale_factor); } //render halo double halo_radius = 0; set_operator(halo_comp_op); for (auto const& glyph_pos : pos) { glyph_info const& glyph = glyph_pos.glyph; halo_radius = glyph.format->halo_radius * scale_factor; // make sure we've got reasonable values. if (halo_radius <= 0.0 || halo_radius > 1024.0) continue; double text_size = glyph.format->text_size * scale_factor; cairo_matrix_t matrix; matrix.xx = text_size * glyph_pos.rot.cos; matrix.xy = text_size * glyph_pos.rot.sin; matrix.yx = text_size * -glyph_pos.rot.sin; matrix.yy = text_size * glyph_pos.rot.cos; matrix.x0 = 0; matrix.y0 = 0; set_font_matrix(matrix); set_font_face(manager, glyph.face); pixel_position new_pos = glyph_pos.pos + glyph.offset.rotate(glyph_pos.rot); glyph_path(glyph.glyph_index, pixel_position(sx + new_pos.x, sy - new_pos.y)); set_line_width(2.0 * halo_radius); set_line_join(ROUND_JOIN); set_color(glyph.format->halo_fill, glyph.format->halo_opacity); stroke(); } set_operator(comp_op); for (auto const& glyph_pos : pos) { glyph_info const& glyph = glyph_pos.glyph; double text_size = glyph.format->text_size * scale_factor; cairo_matrix_t matrix; matrix.xx = text_size * glyph_pos.rot.cos; matrix.xy = text_size * glyph_pos.rot.sin; matrix.yx = text_size * -glyph_pos.rot.sin; matrix.yy = text_size * glyph_pos.rot.cos; matrix.x0 = 0; matrix.y0 = 0; set_font_matrix(matrix); set_font_face(manager, glyph.face); pixel_position new_pos = glyph_pos.pos + glyph.offset.rotate(glyph_pos.rot); set_color(glyph.format->fill, glyph.format->text_opacity); show_glyph(glyph.glyph_index, pixel_position(sx + new_pos.x, sy - new_pos.y)); } }
bool placement_finder::find_point_placement(pixel_position const& pos) { glyph_positions_ptr glyphs = std::make_shared<glyph_positions>(); std::vector<box2d<double> > bboxes; glyphs->reserve(layouts_.glyphs_count()); bboxes.reserve(layouts_.size()); box2d<double> bbox; double max_line_width = 0.0; double first_line_height = -1.0; bool base_point_set = false; for (auto const& layout_ptr : layouts_) { text_layout const& layout = *layout_ptr; rotation const& orientation = layout.orientation(); // Find text origin. pixel_position layout_center = pos + layout.displacement(); if (!base_point_set) { glyphs->set_base_point(layout_center); base_point_set = true; } bbox = layout.bounds(); bbox.re_center(layout_center.x, layout_center.y); /* For point placements it is faster to just check the bounding box. */ if (collision(bbox, layouts_.text(), false)) return false; if (layout.num_lines()) bboxes.push_back(std::move(bbox)); pixel_position layout_offset = layout_center - glyphs->get_base_point(); layout_offset.y = -layout_offset.y; // IMPORTANT NOTE: // x and y are relative to the center of the text // coordinate system: // x: grows from left to right // y: grows from bottom to top (opposite of normal computer graphics) double x, y; // set for upper left corner of text envelope for the first line, top left of first character y = layout.height() / 2.0; for ( auto const& line : layout) { y -= line.height(); //Automatically handles first line differently x = layout.jalign_offset(line.width()); if(line.width() > max_line_width) { max_line_width = line.width(); } if(first_line_height <= 0.0 ) { first_line_height = line.line_height() / scale_factor_; } for (auto const& glyph : line) { // place the character relative to the center of the string envelope glyphs->emplace_back(glyph, (pixel_position(x, y).rotate(orientation)) + layout_offset, orientation); if (glyph.advance()) { //Only advance if glyph is not part of a multiple glyph sequence x += glyph.advance() + glyph.format->character_spacing * scale_factor_; } } } } // add_marker first checks for collision and then updates the detector. if (has_marker_ ) { agg::trans_affine placement_tr; if(text_props_->fit_marker && glyphs->size() > 0) { double padding = 0.1; double width_scale = bbox.width() / marker_box_.width() + 2*padding; // 2 because the text is being padded on both sides. double heigh_scale = bbox.height() / marker_box_.height() + 2*padding; // 2 because the text is being padded on both sides. placement_tr.scale(width_scale, heigh_scale); //now we will need to shift the bbox it will fit the padding placement_tr.translate(- padding, - padding ); } if(!add_marker(glyphs, pos, false, placement_tr)) { return false; } } for (box2d<double> const& bbox : bboxes) { detector_.insert(bbox, layouts_.text()); } placements_.push_back(glyphs); return true; }
pixel_position operator~() const { return pixel_position(x, -y); }
pixel_position operator- (pixel_position const& other) const { return pixel_position(x - other.x, y - other.y); }
pixel_position pixel_position::rotate(rotation const& rot) const { return pixel_position(x * rot.cos - y * rot.sin, x * rot.sin + y * rot.cos); }
void render_point_symbolizer(point_symbolizer const &sym, mapnik::feature_impl &feature, proj_transform const &prj_trans, RendererType &common, F render_marker) { std::string filename = get<std::string,keys::file>(sym,feature, common.vars_); std::shared_ptr<mapnik::marker const> mark = filename.empty() ? std::make_shared<mapnik::marker const>(mapnik::marker_rgba8()) : marker_cache::instance().find(filename, true); if (!mark->is<mapnik::marker_null>()) { value_double opacity = get<value_double,keys::opacity>(sym, feature, common.vars_); value_bool allow_overlap = get<value_bool, keys::allow_overlap>(sym, feature, common.vars_); value_bool ignore_placement = get<value_bool, keys::ignore_placement>(sym, feature, common.vars_); point_placement_enum placement= get<point_placement_enum, keys::point_placement_type>(sym, feature, common.vars_); box2d<double> const& bbox = mark->bounding_box(); coord2d center = bbox.center(); agg::trans_affine tr; auto image_transform = get_optional<transform_type>(sym, keys::image_transform); if (image_transform) evaluate_transform(tr, feature, common.vars_, *image_transform, common.scale_factor_); agg::trans_affine_translation recenter(-center.x, -center.y); agg::trans_affine recenter_tr = recenter * tr; box2d<double> label_ext = bbox * recenter_tr * agg::trans_affine_scaling(common.scale_factor_); mapnik::geometry::geometry<double> const& geometry = feature.get_geometry(); mapnik::geometry::point<double> pt; geometry::geometry_types type = geometry::geometry_type(geometry); if (placement == CENTROID_POINT_PLACEMENT || type == geometry::geometry_types::Point || type == geometry::geometry_types::MultiPoint) { if (!geometry::centroid(geometry, pt)) return; } else if (type == mapnik::geometry::geometry_types::Polygon) { auto const& poly = mapnik::util::get<geometry::polygon<double> >(geometry); geometry::polygon_vertex_adapter<double> va(poly); if (!label::interior_position(va ,pt.x, pt.y)) return; } else { MAPNIK_LOG_WARN(point_symbolizer) << "TODO: unhandled geometry type for point symbolizer"; return; } double x = pt.x; double y = pt.y; double z = 0; prj_trans.backward(x,y,z); common.t_.forward(&x,&y); label_ext.re_center(x,y); if (allow_overlap || common.detector_->has_placement(label_ext)) { render_marker(pixel_position(x, y), *mark, tr, opacity); if (!ignore_placement) common.detector_->insert(label_ext); } } }
pixel_position operator+ (pixel_position const& other) const { return pixel_position(x + other.x, y + other.y); }
void agg_renderer<T>::process(point_symbolizer const& sym, mapnik::feature_ptr const& feature, proj_transform const& prj_trans) { std::string filename = path_processor_type::evaluate(*sym.get_filename(), *feature); boost::optional<mapnik::marker_ptr> marker; if ( !filename.empty() ) { marker = marker_cache::instance()->find(filename, true); } else { marker.reset(boost::make_shared<mapnik::marker>()); } if (marker) { double w = (*marker)->width(); double h = (*marker)->height(); agg::trans_affine tr; boost::array<double,6> const& m = sym.get_transform(); tr.load_from(&m[0]); double px0 = - 0.5 * w; double py0 = - 0.5 * h; double px1 = 0.5 * w; double py1 = 0.5 * h; double px2 = px1; double py2 = py0; double px3 = px0; double py3 = py1; tr.transform(&px0,&py0); tr.transform(&px1,&py1); tr.transform(&px2,&py2); tr.transform(&px3,&py3); box2d<double> label_ext (px0, py0, px1, py1); label_ext.expand_to_include(px2, py2); label_ext.expand_to_include(px3, py3); for (unsigned i=0; i<feature->num_geometries(); ++i) { geometry_type const& geom = feature->get_geometry(i); double x; double y; double z=0; if (sym.get_point_placement() == CENTROID_POINT_PLACEMENT) geom.label_position(&x, &y); else geom.label_interior_position(&x, &y); prj_trans.backward(x,y,z); t_.forward(&x,&y); label_ext.re_center(x,y); if (sym.get_allow_overlap() || detector_->has_placement(label_ext)) { render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h) ,**marker,tr, sym.get_opacity()); if (!sym.get_ignore_placement()) detector_->insert(label_ext); metawriter_with_properties writer = sym.get_metawriter(); if (writer.first) writer.first->add_box(label_ext, *feature, t_, writer.second); } } } }
pixel_position operator* (double other) const { return pixel_position(x * other, y * other); }
void agg_renderer<T>::process(point_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { std::string filename = path_processor_type::evaluate(*sym.get_filename(), feature); boost::optional<mapnik::marker_ptr> marker; if ( !filename.empty() ) { marker = marker_cache::instance()->find(filename, true); } else { marker.reset(boost::make_shared<mapnik::marker>()); } if (marker) { box2d<double> const& bbox = (*marker)->bounding_box(); coord2d center = bbox.center(); agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_image_transform()); agg::trans_affine_translation recenter(-center.x, -center.y); agg::trans_affine recenter_tr = recenter * tr; box2d<double> label_ext = bbox * recenter_tr; for (unsigned i=0; i<feature.num_geometries(); ++i) { geometry_type const& geom = feature.get_geometry(i); double x; double y; double z=0; if (sym.get_point_placement() == CENTROID_POINT_PLACEMENT) { if (!label::centroid(geom, x, y)) return; } else { if (!label::interior_position(geom ,x, y)) return; } prj_trans.backward(x,y,z); t_.forward(&x,&y); label_ext.re_center(x,y); if (sym.get_allow_overlap() || detector_->has_placement(label_ext)) { render_marker(pixel_position(x, y), **marker, tr, sym.get_opacity(), sym.comp_op()); if (!sym.get_ignore_placement()) detector_->insert(label_ext); } } } }
inline pixel_position operator* (double factor, pixel_position const& pos) { return pixel_position(factor * pos.x, factor * pos.y); }
void render_point_symbolizer(point_symbolizer const &sym, mapnik::feature_impl &feature, proj_transform const &prj_trans, RendererType &common, F render_marker) { std::string filename = get<std::string>(sym, keys::file, feature, common.vars_); boost::optional<mapnik::marker_ptr> marker = filename.empty() ? std::make_shared<mapnik::marker>() : marker_cache::instance().find(filename, true); if (marker) { double opacity = get<double>(sym,keys::opacity,feature, common.vars_, 1.0); bool allow_overlap = get<bool>(sym, keys::allow_overlap, feature, common.vars_, false); bool ignore_placement = get<bool>(sym, keys::ignore_placement, feature, common.vars_, false); point_placement_enum placement= get<point_placement_enum>(sym, keys::point_placement_type, feature, common.vars_, CENTROID_POINT_PLACEMENT); box2d<double> const& bbox = (*marker)->bounding_box(); coord2d center = bbox.center(); agg::trans_affine tr; auto image_transform = get_optional<transform_type>(sym, keys::image_transform); if (image_transform) evaluate_transform(tr, feature, common.vars_, *image_transform); agg::trans_affine_translation recenter(-center.x, -center.y); agg::trans_affine recenter_tr = recenter * tr; box2d<double> label_ext = bbox * recenter_tr * agg::trans_affine_scaling(common.scale_factor_); for (std::size_t i=0; i<feature.num_geometries(); ++i) { geometry_type const& geom = feature.get_geometry(i); double x; double y; double z=0; if (placement == CENTROID_POINT_PLACEMENT) { if (!label::centroid(geom, x, y)) return; } else { if (!label::interior_position(geom ,x, y)) return; } prj_trans.backward(x,y,z); common.t_.forward(&x,&y); label_ext.re_center(x,y); if (allow_overlap || common.detector_->has_placement(label_ext)) { render_marker(pixel_position(x, y), **marker, tr, opacity); if (!ignore_placement) common.detector_->insert(label_ext); } } } }