void raster_colorizer::colorize(raster_ptr const& raster, feature_impl const& f) const { unsigned *imageData = raster->data_.getData(); int len = raster->data_.width() * raster->data_.height(); bool hasNoData = false; float noDataValue = 0; //std::map<std::string,value>::const_iterator fi = Props.find("NODATA"); if (f.has_key("NODATA")) { hasNoData = true; noDataValue = static_cast<float>(f.get("NODATA").to_double()); } for (int i=0; i<len; ++i) { // the GDAL plugin reads single bands as floats float value = *reinterpret_cast<float *> (&imageData[i]); if (hasNoData && noDataValue == value) imageData[i] = color(0,0,0,0).rgba(); else imageData[i] = get_color(value).rgba(); } }
static void apply(feature_impl const& feature, proj_transform const& prj_trans, view_transform const& view_trans, double height, double shadow_angle, double shadow_length, double opacity, F1 const & face_func, F2 const & frame_func, F3 const & roof_func, F4 const & shadow_func) { auto const& geom = feature.get_geometry(); if (geom.is<geometry::polygon<double>>()) { auto const& poly = geom.get<geometry::polygon<double>>(); vertex_adapter_type va(poly); transform_path_type transformed(view_trans, va, prj_trans); make_building(transformed, height, shadow_angle, shadow_length, opacity, face_func, frame_func, roof_func, shadow_func); } else if (geom.is<geometry::multi_polygon<double>>()) { auto const& multi_poly = geom.get<geometry::multi_polygon<double>>(); for (auto const& poly : multi_poly) { vertex_adapter_type va(poly); transform_path_type transformed(view_trans, va, prj_trans); make_building(transformed, height, shadow_angle, shadow_length, opacity, face_func, frame_func, roof_func, shadow_func); } } }
void apply_markers_multi(feature_impl const& feature, attributes const& vars, Converter & converter, markers_symbolizer const& sym) { std::size_t geom_count = feature.paths().size(); if (geom_count == 1) { converter.apply(feature.paths()[0]); } else if (geom_count > 1) { marker_multi_policy_enum multi_policy = get<marker_multi_policy_enum>(sym, keys::markers_multipolicy, feature, vars, MARKER_EACH_MULTI); marker_placement_enum placement = get<marker_placement_enum>(sym, keys::markers_placement_type, feature, vars, MARKER_POINT_PLACEMENT); if (placement == MARKER_POINT_PLACEMENT && multi_policy == MARKER_WHOLE_MULTI) { double x, y; if (label::centroid_geoms(feature.paths().begin(), feature.paths().end(), x, y)) { geometry_type pt(geometry_type::types::Point); pt.move_to(x, y); // unset any clipping since we're now dealing with a point converter.template unset<clip_poly_tag>(); converter.apply(pt); } } else if ((placement == MARKER_POINT_PLACEMENT || placement == MARKER_INTERIOR_PLACEMENT) && multi_policy == MARKER_LARGEST_MULTI) { // Only apply to path with largest envelope area // TODO: consider using true area for polygon types double maxarea = 0; geometry_type const* largest = 0; for (geometry_type const& geom : feature.paths()) { const box2d<double>& env = geom.envelope(); double area = env.width() * env.height(); if (area > maxarea) { maxarea = area; largest = &geom; } } if (largest) { converter.apply(*largest); } } else { if (multi_policy != MARKER_EACH_MULTI && placement != MARKER_POINT_PLACEMENT) { MAPNIK_LOG_WARN(marker_symbolizer) << "marker_multi_policy != 'each' has no effect with marker_placement != 'point'"; } for (geometry_type const& path : feature.paths()) { converter.apply(path); } } } }
void render_group_symbolizer(group_symbolizer const& sym, feature_impl & feature, attributes const& vars, proj_transform const& prj_trans, box2d<double> const& clipping_extent, renderer_common & common, F render_thunks) { // find all column names referenced in the group rules and symbolizers std::set<std::string> columns; group_attribute_collector column_collector(columns, false); column_collector(sym); group_symbolizer_properties_ptr props = get<group_symbolizer_properties_ptr>(sym, keys::group_properties); // create a new context for the sub features of this group context_ptr sub_feature_ctx = std::make_shared<mapnik::context_type>(); // populate new context with column names referenced in the group rules and symbolizers for (auto const& col_name : columns) { sub_feature_ctx->push(col_name); } // keep track of the sub features that we'll want to symbolize // along with the group rules that they matched std::vector< std::pair<group_rule_ptr, feature_ptr> > matches; // create a copied 'virtual' common renderer for processing sub feature symbolizers // create an empty detector for it, so we are sure we won't hit anything virtual_renderer_common virtual_renderer(common); // keep track of which lists of render thunks correspond to // entries in the group_layout_manager. std::vector<render_thunk_list> layout_thunks; size_t num_layout_thunks = 0; // layout manager to store and arrange bboxes of matched features group_layout_manager layout_manager(props->get_layout(), pixel_position(common.width_ / 2.0, common.height_ / 2.0)); // run feature or sub feature through the group rules & symbolizers // for each index value in the range value_integer start = get<value_integer>(sym, keys::start_column); value_integer end = start + get<value_integer>(sym, keys::num_columns); for (value_integer col_idx = start; col_idx < end; ++col_idx) { // create sub feature with indexed column values feature_ptr sub_feature = feature_factory::create(sub_feature_ctx, col_idx); // copy the necessary columns to sub feature for(auto const& col_name : columns) { if (col_name.find('%') != std::string::npos) { if (col_name.size() == 1) { // column name is '%' by itself, so give the index as the value sub_feature->put(col_name, col_idx); } else { // indexed column std::string col_idx_str; if (mapnik::util::to_string(col_idx_str,col_idx)) { std::string col_idx_name = col_name; boost::replace_all(col_idx_name, "%", col_idx_str); sub_feature->put(col_name, feature.get(col_idx_name)); } } } else { // non-indexed column sub_feature->put(col_name, feature.get(col_name)); } } // add a single point geometry at pixel origin double x = common.width_ / 2.0, y = common.height_ / 2.0, z = 0.0; common.t_.backward(&x, &y); prj_trans.forward(x, y, z); // note that we choose a point in the middle of the screen to // try to ensure that we don't get edge artefacts due to any // symbolizers with avoid-edges set: only the avoid-edges of // the group symbolizer itself should matter. geometry::point<double> origin_pt(x,y); sub_feature->set_geometry(origin_pt); // get the layout for this set of properties for (auto const& rule : props->get_rules()) { if (util::apply_visitor(evaluate<feature_impl,value_type,attributes>(*sub_feature,common.vars_), *(rule->get_filter())).to_bool()) { // add matched rule and feature to the list of things to draw matches.emplace_back(rule, sub_feature); // construct a bounding box around all symbolizers for the matched rule bound_box bounds; render_thunk_list thunks; render_thunk_extractor extractor(bounds, thunks, *sub_feature, common.vars_, prj_trans, virtual_renderer, clipping_extent); for (auto const& _sym : *rule) { // TODO: construct layout and obtain bounding box util::apply_visitor(extractor, _sym); } // add the bounding box to the layout manager layout_manager.add_member_bound_box(bounds); layout_thunks.emplace_back(std::move(thunks)); ++num_layout_thunks; break; } } } // create a symbolizer helper group_symbolizer_helper helper(sym, feature, vars, prj_trans, common.width_, common.height_, common.scale_factor_, common.t_, *common.detector_, clipping_extent); for (size_t i = 0; i < matches.size(); ++i) { group_rule_ptr match_rule = matches[i].first; feature_ptr match_feature = matches[i].second; value_unicode_string rpt_key_value = ""; // get repeat key from matched group rule expression_ptr rpt_key_expr = match_rule->get_repeat_key(); // if no repeat key was defined, use default from group symbolizer if (!rpt_key_expr) { rpt_key_expr = get<expression_ptr>(sym, keys::repeat_key); } // evaluate the repeat key with the matched sub feature if we have one if (rpt_key_expr) { rpt_key_value = util::apply_visitor(evaluate<feature_impl,value_type,attributes>(*match_feature,common.vars_), *rpt_key_expr).to_unicode(); } helper.add_box_element(layout_manager.offset_box_at(i), rpt_key_value); } pixel_position_list positions = helper.get(); for (pixel_position const& pos : positions) { for (size_t layout_i = 0; layout_i < num_layout_thunks; ++layout_i) { pixel_position const& offset = layout_manager.offset_at(layout_i); pixel_position render_offset = pos + offset; render_thunks(layout_thunks[layout_i], render_offset); } } }
void apply_markers_multi(feature_impl const& feature, attributes const& vars, Converter & converter, symbolizer_base const& sym) { using vertex_converter_type = Converter; using apply_vertex_converter_type = detail::apply_vertex_converter<vertex_converter_type>; using vertex_processor_type = geometry::vertex_processor<apply_vertex_converter_type>; auto const& geom = feature.get_geometry(); geometry::geometry_types type = geometry::geometry_type(geom); if (type == geometry::geometry_types::Point || type == geometry::geometry_types::LineString || type == geometry::geometry_types::Polygon) { apply_vertex_converter_type apply(converter); mapnik::util::apply_visitor(vertex_processor_type(apply), geom); } else { marker_multi_policy_enum multi_policy = get<marker_multi_policy_enum, keys::markers_multipolicy>(sym, feature, vars); marker_placement_enum placement = get<marker_placement_enum, keys::markers_placement_type>(sym, feature, vars); if (placement == MARKER_POINT_PLACEMENT && multi_policy == MARKER_WHOLE_MULTI) { geometry::point<double> pt; // test if centroid is contained by bounding box if (geometry::centroid(geom, pt) && converter.disp_.args_.bbox.contains(pt.x, pt.y)) { // unset any clipping since we're now dealing with a point converter.template unset<clip_poly_tag>(); geometry::point_vertex_adapter<double> va(pt); converter.apply(va); } } else if ((placement == MARKER_POINT_PLACEMENT || placement == MARKER_INTERIOR_PLACEMENT) && multi_policy == MARKER_LARGEST_MULTI) { // Only apply to path with largest envelope area // TODO: consider using true area for polygon types if (type == geometry::geometry_types::MultiPolygon) { geometry::multi_polygon<double> const& multi_poly = mapnik::util::get<geometry::multi_polygon<double> >(geom); double maxarea = 0; geometry::polygon<double> const* largest = 0; for (geometry::polygon<double> const& poly : multi_poly) { box2d<double> bbox = geometry::envelope(poly); geometry::polygon_vertex_adapter<double> va(poly); double area = bbox.width() * bbox.height(); if (area > maxarea) { maxarea = area; largest = &poly; } } if (largest) { geometry::polygon_vertex_adapter<double> va(*largest); converter.apply(va); } } else { MAPNIK_LOG_WARN(marker_symbolizer) << "TODO: if you get here -> open an issue"; } } else { if (multi_policy != MARKER_EACH_MULTI && placement != MARKER_POINT_PLACEMENT) { MAPNIK_LOG_WARN(marker_symbolizer) << "marker_multi_policy != 'each' has no effect with marker_placement != 'point'"; } apply_vertex_converter_type apply(converter); mapnik::util::apply_visitor(vertex_processor_type(apply), geom); } } }
void agg_renderer<T>::process(markers_symbolizer const& sym, feature_impl & feature, proj_transform const& prj_trans) { typedef agg::rgba8 color_type; typedef agg::order_rgba order_type; typedef agg::pixel32_type pixel_type; typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender typedef agg::rendering_buffer buf_type; typedef agg::pixfmt_custom_blend_rgba<blender_type, buf_type> pixfmt_comp_type; typedef agg::renderer_base<pixfmt_comp_type> renderer_base; typedef label_collision_detector4 detector_type; typedef boost::mpl::vector<clip_line_tag,clip_poly_tag,transform_tag,smooth_tag> conv_types; std::string filename = path_processor_type::evaluate(*sym.get_filename(), feature); if (!filename.empty()) { boost::optional<marker_ptr> mark = mapnik::marker_cache::instance().find(filename, true); if (mark && *mark) { ras_ptr->reset(); ras_ptr->gamma(agg::gamma_power()); agg::trans_affine geom_tr; evaluate_transform(geom_tr, feature, sym.get_transform()); agg::trans_affine tr = agg::trans_affine_scaling(scale_factor_); if ((*mark)->is_vector()) { using namespace mapnik::svg; typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type; typedef agg::pod_bvector<path_attributes> svg_attribute_type; typedef svg_renderer_agg<svg_path_adapter, svg_attribute_type, renderer_type, pixfmt_comp_type > svg_renderer_type; typedef vector_markers_rasterizer_dispatch<buf_type, svg_renderer_type, rasterizer, detector_type > dispatch_type; boost::optional<svg_path_ptr> const& stock_vector_marker = (*mark)->get_vector_data(); expression_ptr const& width_expr = sym.get_width(); expression_ptr const& height_expr = sym.get_height(); // special case for simple ellipse markers // to allow for full control over rx/ry dimensions if (filename == "shape://ellipse" && (width_expr || height_expr)) { svg_storage_type marker_ellipse; vertex_stl_adapter<svg_path_storage> stl_storage(marker_ellipse.source()); svg_path_adapter svg_path(stl_storage); build_ellipse(sym, feature, marker_ellipse, svg_path); svg_attribute_type attributes; bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym); svg_renderer_type svg_renderer(svg_path, result ? attributes : (*stock_vector_marker)->attributes()); evaluate_transform(tr, feature, sym.get_image_transform()); box2d<double> bbox = marker_ellipse.bounding_box(); coord2d center = bbox.center(); agg::trans_affine_translation recenter(-center.x, -center.y); agg::trans_affine marker_trans = recenter * tr; buf_type render_buffer(current_buffer_->raw_data(), width_, height_, width_ * 4); dispatch_type rasterizer_dispatch(render_buffer,svg_renderer,*ras_ptr, bbox, marker_trans, sym, *detector_, scale_factor_); vertex_converter<box2d<double>, dispatch_type, markers_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) { eGeomType type = feature.paths()[0].type(); if (type == Polygon) converter.template set<clip_poly_tag>(); // line clipping disabled due to https://github.com/mapnik/mapnik/issues/1426 //else if (type == LineString) // converter.template set<clip_line_tag>(); // don't clip if type==Point } converter.template set<transform_tag>(); //always transform if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter apply_markers_multi(feature, converter, sym); } else { box2d<double> const& bbox = (*mark)->bounding_box(); setup_transform_scaling(tr, bbox, feature, sym); evaluate_transform(tr, feature, sym.get_image_transform()); coord2d center = bbox.center(); agg::trans_affine_translation recenter(-center.x, -center.y); agg::trans_affine marker_trans = recenter * tr; vertex_stl_adapter<svg_path_storage> stl_storage((*stock_vector_marker)->source()); svg_path_adapter svg_path(stl_storage); svg_attribute_type attributes; bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym); svg_renderer_type svg_renderer(svg_path, result ? attributes : (*stock_vector_marker)->attributes()); buf_type render_buffer(current_buffer_->raw_data(), width_, height_, width_ * 4); dispatch_type rasterizer_dispatch(render_buffer,svg_renderer,*ras_ptr, bbox, marker_trans, sym, *detector_, scale_factor_); vertex_converter<box2d<double>, dispatch_type, markers_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) { eGeomType type = feature.paths()[0].type(); if (type == Polygon) converter.template set<clip_poly_tag>(); // line clipping disabled due to https://github.com/mapnik/mapnik/issues/1426 //else if (type == LineString) // converter.template set<clip_line_tag>(); // don't clip if type==Point } converter.template set<transform_tag>(); //always transform if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter apply_markers_multi(feature, converter, sym); } } else // raster markers { box2d<double> const& bbox = (*mark)->bounding_box(); setup_transform_scaling(tr, bbox, feature, sym); evaluate_transform(tr, feature, sym.get_image_transform()); coord2d center = bbox.center(); agg::trans_affine_translation recenter(-center.x, -center.y); agg::trans_affine marker_trans = recenter * tr; boost::optional<mapnik::image_ptr> marker = (*mark)->get_bitmap_data(); typedef raster_markers_rasterizer_dispatch<buf_type,rasterizer, detector_type> dispatch_type; buf_type render_buffer(current_buffer_->raw_data(), width_, height_, width_ * 4); dispatch_type rasterizer_dispatch(render_buffer,*ras_ptr, **marker, marker_trans, sym, *detector_, scale_factor_); vertex_converter<box2d<double>, dispatch_type, markers_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) { eGeomType type = feature.paths()[0].type(); if (type == Polygon) converter.template set<clip_poly_tag>(); // line clipping disabled due to https://github.com/mapnik/mapnik/issues/1426 //else if (type == LineString) // converter.template set<clip_line_tag>(); // don't clip if type==Point } converter.template set<transform_tag>(); //always transform if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter apply_markers_multi(feature, converter, sym); } } } }