void grid_renderer<T>::process(building_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans)
{
    using pixfmt_type = typename grid_renderer_base_type::pixfmt_type;
    using color_type = typename grid_renderer_base_type::pixfmt_type::color_type;
    using renderer_type = agg::renderer_scanline_bin_solid<grid_renderer_base_type>;
    using transform_path_type = transform_path_adapter<view_transform, vertex_adapter>;
    agg::scanline_bin sl;

    grid_rendering_buffer buf(pixmap_.raw_data(), common_.width_, common_.height_, common_.width_);
    pixfmt_type pixf(buf);

    grid_renderer_base_type renb(pixf);
    renderer_type ren(renb);

    ras_ptr->reset();

    double height = get<value_double>(sym, keys::height, feature, common_.vars_, 0.0);

    render_building_symbolizer(
        feature, height,
        [&](path_type const& faces)
        {
            vertex_adapter va(faces);
            transform_path_type faces_path (common_.t_,va,prj_trans);
            ras_ptr->add_path(faces_path);
            ren.color(color_type(feature.id()));
            agg::render_scanlines(*ras_ptr, sl, ren);
            ras_ptr->reset();
        },
        [&](path_type const& frame)
        {
            vertex_adapter va(frame);
            transform_path_type path(common_.t_,va,prj_trans);
            agg::conv_stroke<transform_path_type> stroke(path);
            ras_ptr->add_path(stroke);
            ren.color(color_type(feature.id()));
            agg::render_scanlines(*ras_ptr, sl, ren);
            ras_ptr->reset();
        },
        [&](path_type const& roof)
        {
            vertex_adapter va(roof);
            transform_path_type roof_path (common_.t_,va,prj_trans);
            ras_ptr->add_path(roof_path);
            ren.color(color_type(feature.id()));
            agg::render_scanlines(*ras_ptr, sl, ren);
        });

    pixmap_.add_feature(feature);
}
void grid_renderer<T>::process(text_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans)
{
    text_symbolizer_helper<face_manager<freetype_engine>,
        label_collision_detector4> helper(
            sym, feature, prj_trans,
            width_, height_,
            scale_factor_ * (1.0/pixmap_.get_resolution()),
            t_, font_manager_, *detector_,
            query_extent_);
    bool placement_found = false;

    text_renderer<T> ren(pixmap_,
                         font_manager_,
                         sym.get_halo_rasterizer(),
                         sym.comp_op(),
                         scale_factor_);

    while (helper.next()) {
        placement_found = true;
        placements_type const& placements = helper.placements();
        for (unsigned int ii = 0; ii < placements.size(); ++ii)
        {
            ren.prepare_glyphs(placements[ii]);
            ren.render_id(feature.id(), placements[ii].center);
        }
    }
    if (placement_found) pixmap_.add_feature(feature);

}
void render_raster_marker(RendererType ren,
                          RasterizerType & ras,
                          image_data_rgba8 & src,
                          mapnik::feature_impl const& feature,
                          agg::trans_affine const& marker_tr,
                          double opacity)
{
    using color_type = typename RendererType::color_type;
    agg::scanline_bin sl;
    double width  = src.width();
    double height = src.height();
    double p[8];
    p[0] = 0;     p[1] = 0;
    p[2] = width; p[3] = 0;
    p[4] = width; p[5] = height;
    p[6] = 0;     p[7] = height;
    marker_tr.transform(&p[0], &p[1]);
    marker_tr.transform(&p[2], &p[3]);
    marker_tr.transform(&p[4], &p[5]);
    marker_tr.transform(&p[6], &p[7]);
    ras.move_to_d(p[0],p[1]);
    ras.line_to_d(p[2],p[3]);
    ras.line_to_d(p[4],p[5]);
    ras.line_to_d(p[6],p[7]);
    ren.color(color_type(feature.id()));
    agg::render_scanlines(ras, sl, ren);
}
void grid_renderer<T>::process(text_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans)
{
    agg::trans_affine tr;
    auto transform = get_optional<transform_type>(sym, keys::geometry_transform);
    if (transform) evaluate_transform(tr, feature, common_.vars_, *transform, common_.scale_factor_);
    text_symbolizer_helper helper(
            sym, feature, common_.vars_, prj_trans,
            common_.width_, common_.height_,
            common_.scale_factor_ * (1.0/pixmap_.get_resolution()),
            common_.t_, common_.font_manager_, *common_.detector_,
            common_.query_extent_, tr);
    bool placement_found = false;

    composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature, common_.vars_, src_over);

    grid_text_renderer<T> ren(pixmap_,
                              comp_op,
                              common_.scale_factor_);

    placements_list const& placements = helper.get();
    value_integer feature_id = feature.id();

    for (glyph_positions_ptr glyphs : placements)
    {
        ren.render(*glyphs, feature_id);
        placement_found = true;
    }
    if (placement_found)
    {
        pixmap_.add_feature(feature);
    }
}
void grid_renderer<T>::process(polygon_pattern_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<marker_ptr> mark = marker_cache::instance().find(filename,true);
    if (!mark) return;

    if (!(*mark)->is_bitmap())
    {
        MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Only images (not '" << filename << "') are supported in the line_pattern_symbolizer";
        return;
    }

    boost::optional<image_ptr> pat = (*mark)->get_bitmap_data();
    if (!pat) return;

    ras_ptr->reset();

    agg::trans_affine tr;
    evaluate_transform(tr, feature, sym.get_transform());

    typedef boost::mpl::vector<clip_poly_tag,transform_tag,affine_transform_tag,smooth_tag> conv_types;
    vertex_converter<box2d<double>, grid_rasterizer, polygon_pattern_symbolizer,
                     CoordTransform, proj_transform, agg::trans_affine, conv_types>
        converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);

    if (prj_trans.equal() && sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true)
    converter.set<transform_tag>(); //always transform
    converter.set<affine_transform_tag>();
    if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter


    for ( geometry_type & geom : feature.paths())
    {
        if (geom.size() > 2)
        {
            converter.apply(geom);
        }
    }
    typedef typename grid_renderer_base_type::pixfmt_type pixfmt_type;
    typedef typename grid_renderer_base_type::pixfmt_type::color_type color_type;
    typedef agg::renderer_scanline_bin_solid<grid_renderer_base_type> renderer_type;

    grid_rendering_buffer buf(pixmap_.raw_data(), width_, height_, width_);
    pixfmt_type pixf(buf);

    grid_renderer_base_type renb(pixf);
    renderer_type ren(renb);

    // render id
    ren.color(color_type(feature.id()));
    agg::scanline_bin sl;
    ras_ptr->filling_rule(agg::fill_even_odd);
    agg::render_scanlines(*ras_ptr, sl, ren);

    // add feature properties to grid cache
    pixmap_.add_feature(feature);
}
Exemple #6
0
void hit_grid<T>::add_feature(mapnik::feature_impl const& feature)
{
    value_type feature_id = feature.id();
    // avoid adding duplicate features (e.g. in the case of both a line symbolizer and a polygon symbolizer)
    typename feature_key_type::const_iterator feature_pos = f_keys_.find(feature_id);
    if (feature_pos != f_keys_.end())
    {
        return;
    }

    if (ctx_->size() == 0)
    {
        context_type::map_type::const_iterator itr = feature.context()->begin();
        context_type::map_type::const_iterator end = feature.context()->end();
        for ( ;itr!=end; ++itr)
        {
            ctx_->add(itr->first,itr->second);
        }
    }
    // NOTE: currently lookup keys must be strings,
    // but this should be revisited
    lookup_type lookup_value;
    if (key_ == id_name_)
    {
        mapnik::util::to_string(lookup_value,feature_id);
    }
    else
    {
        if (feature.has_key(key_))
        {
            lookup_value = feature.get(key_).to_string();
        }
        else
        {
            MAPNIK_LOG_DEBUG(grid) << "hit_grid: Should not get here: key '" << key_ << "' not found in feature properties";
        }
    }

    if (!lookup_value.empty())
    {
        // TODO - consider shortcutting f_keys if feature_id == lookup_value
        // create a mapping between the pixel id and the feature key
        f_keys_.emplace(feature_id,lookup_value);
        // if extra fields have been supplied, push them into grid memory
        if (!names_.empty())
        {
            // it is ~ 2x faster to copy feature attributes compared
            // to building up a in-memory cache of feature_ptrs
            // https://github.com/mapnik/mapnik/issues/1198
            mapnik::feature_ptr feature2(mapnik::feature_factory::create(ctx_,feature_id));
            feature2->set_data(feature.get_data());
            features_.emplace(lookup_value,feature2);
        }
    }
    else
    {
        MAPNIK_LOG_DEBUG(grid) << "hit_grid: Warning - key '" << key_ << "' was blank for " << feature;
    }
}
        void start_tile_feature(mapnik::feature_impl const& feature)
        {
            current_feature_ = current_layer_->add_features();
            x_ = y_ = 0;

            // TODO - encode as sint64: (n << 1) ^ ( n >> 63)
            // test current behavior with negative numbers
            current_feature_->set_id(feature.id());

            feature_kv_iterator itr = feature.begin();
            feature_kv_iterator end = feature.end();
            for ( ;itr!=end; ++itr)
            {
                std::string const& name = MAPNIK_GET<0>(*itr);
                mapnik::value const& val = MAPNIK_GET<1>(*itr);
                if (!val.is_null())
                {
                    // Insert the key index
                    keys_container::const_iterator key_itr = keys_.find(name);
                    if (key_itr == keys_.end())
                    {
                        // The key doesn't exist yet in the dictionary.
                        current_layer_->add_keys(name.c_str(), name.length());
                        size_t index = keys_.size();
                        keys_.insert(keys_container::value_type(name, index));
                        current_feature_->add_tags(index);
                    }
                    else
                    {
                        current_feature_->add_tags(key_itr->second);
                    }

                    // Insert the value index
                    values_container::const_iterator val_itr = values_.find(val);
                    if (val_itr == values_.end())
                    {
                        // The value doesn't exist yet in the dictionary.
                        to_tile_value visitor(current_layer_->add_values());
#if MAPNIK_VERSION >= 300000
                        MAPNIK_APPLY_VISITOR(visitor, val);
#else
                        MAPNIK_APPLY_VISITOR(visitor, val.base());
#endif
                        size_t index = values_.size();
                        values_.insert(values_container::value_type(val, index));
                        current_feature_->add_tags(index);
                    }
                    else
                    {
                        current_feature_->add_tags(val_itr->second);
                    }
                }
            }
        }
void  grid_renderer<T>::process(shield_symbolizer const& sym,
                                mapnik::feature_impl & feature,
                                proj_transform const& prj_trans)
{
    shield_symbolizer_helper<face_manager<freetype_engine>,
        label_collision_detector4> helper(
            sym, feature, prj_trans,
            width_, height_,
            scale_factor_,
            t_, font_manager_, *detector_,
            query_extent_);
    bool placement_found = false;

    text_renderer<T> ren(pixmap_,
                         font_manager_,
                         sym.get_halo_rasterizer(),
                         sym.comp_op(),
                         scale_factor_);

    text_placement_info_ptr placement;
    while (helper.next())
    {
        placement_found = true;
        placements_type const& placements = helper.placements();
        for (unsigned int ii = 0; ii < placements.size(); ++ii)
        {
            // get_marker_position returns (minx,miny) corner position,
            // while (currently only) agg_renderer::render_marker newly
            // expects center position;
            // until all renderers and shield_symbolizer_helper are
            // modified accordingly, we must adjust the position here
            pixel_position pos = helper.get_marker_position(placements[ii]);
            pos.x += 0.5 * helper.get_marker_width();
            pos.y += 0.5 * helper.get_marker_height();
            render_marker(feature,
                          pixmap_.get_resolution(),
                          pos,
                          helper.get_marker(),
                          helper.get_image_transform(),
                          sym.get_opacity(),
                          sym.comp_op());

            ren.prepare_glyphs(placements[ii]);
            ren.render_id(feature.id(), placements[ii].center);
        }
    }
    if (placement_found)
        pixmap_.add_feature(feature);
}
void grid_renderer<T>::process(polygon_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans)
{
    typedef agg::renderer_scanline_bin_solid<grid_renderer_base_type> renderer_type;
    typedef typename grid_renderer_base_type::pixfmt_type pixfmt_type;
    typedef typename grid_renderer_base_type::pixfmt_type::color_type color_type;

    ras_ptr->reset();

    agg::trans_affine tr;
    evaluate_transform(tr, feature, sym.get_transform(), scale_factor_);

    typedef boost::mpl::vector<clip_poly_tag,transform_tag,affine_transform_tag,simplify_tag,smooth_tag> conv_types;
    vertex_converter<box2d<double>, grid_rasterizer, polygon_symbolizer,
                     CoordTransform, proj_transform, agg::trans_affine, conv_types>
        converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);

    if (prj_trans.equal() && sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true)
    converter.set<transform_tag>(); //always transform
    converter.set<affine_transform_tag>();
    if (sym.simplify_tolerance() > 0.0) converter.set<simplify_tag>(); // optional simplify converter
    if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter


    for ( geometry_type & geom : feature.paths())
    {
        if (geom.size() > 2)
        {
            converter.apply(geom);
        }
    }

    grid_rendering_buffer buf(pixmap_.raw_data(), width_, height_, width_);
    pixfmt_type pixf(buf);

    grid_renderer_base_type renb(pixf);
    renderer_type ren(renb);

    // render id
    ren.color(color_type(feature.id()));
    agg::scanline_bin sl;
    ras_ptr->filling_rule(agg::fill_even_odd);
    agg::render_scanlines(*ras_ptr, sl, ren);

    // add feature properties to grid cache
    pixmap_.add_feature(feature);
}
void  grid_renderer<T>::process(shield_symbolizer const& sym,
                                mapnik::feature_impl & feature,
                                proj_transform const& prj_trans)
{
    agg::trans_affine tr;
    auto transform = get_optional<transform_type>(sym, keys::geometry_transform);
    if (transform) evaluate_transform(tr, feature, common_.vars_, *transform, common_.scale_factor_);

    text_symbolizer_helper helper(
            sym, feature, common_.vars_, prj_trans,
            common_.width_, common_.height_,
            common_.scale_factor_,
            common_.t_, common_.font_manager_, *common_.detector_,
            common_.query_extent_, tr);
    bool placement_found = false;

    composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature, common_.vars_, src_over);
    double opacity = get<double>(sym, keys::opacity, feature, common_.vars_, 1.0);

    grid_text_renderer<T> ren(pixmap_,
                              comp_op,
                              common_.scale_factor_);

    placements_list const& placements = helper.get();
    value_integer feature_id = feature.id();

    for (auto const& glyphs : placements)
    {
        marker_info_ptr mark = glyphs->get_marker();
        if (mark)
        {
            render_marker(feature,
                          glyphs->marker_pos(),
                          *mark->marker_,
                          mark->transform_,
                          opacity, comp_op);
        }
        ren.render(*glyphs, feature_id);
        placement_found = true;
    }
    if (placement_found)
    {
        pixmap_.add_feature(feature);
    }
}
Exemple #11
0
void grid_renderer<T>::process(text_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans)
{
    box2d<double> clip_box = clipping_extent(common_);
    agg::trans_affine tr;
    auto transform = get_optional<transform_type>(sym, keys::geometry_transform);
    if (transform) evaluate_transform(tr, feature, common_.vars_, *transform, common_.scale_factor_);
    text_symbolizer_helper helper(
        sym, feature, common_.vars_, prj_trans,
        common_.width_, common_.height_,
        common_.scale_factor_,
        common_.t_, common_.font_manager_, *common_.detector_,
        clip_box, tr, common_.symbol_cache_);
    bool placement_found = false;

    composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature, common_.vars_, src_over);

    grid_text_renderer<T> ren(pixmap_,
                              comp_op,
                              common_.scale_factor_);

    auto halo_transform = get_optional<transform_type>(sym, keys::halo_transform);
    if (halo_transform)
    {
        agg::trans_affine halo_affine_transform;
        evaluate_transform(halo_affine_transform, feature, common_.vars_, *halo_transform, common_.scale_factor_);
        ren.set_halo_transform(halo_affine_transform);
    }

    placements_list const& placements = helper.get();
    value_integer feature_id = feature.id();

    for (auto const& glyphs : placements)
    {
        ren.render(*glyphs, feature_id);
        placement_found = true;
    }
    if (placement_found)
    {
        pixmap_.add_feature(feature);
    }
}
void  grid_renderer<T>::process(shield_symbolizer const& sym,
                                mapnik::feature_impl & feature,
                                proj_transform const& prj_trans)
{
    text_symbolizer_helper helper(
            sym, feature, common_.vars_, prj_trans,
            common_.width_, common_.height_,
            common_.scale_factor_,
            common_.t_, common_.font_manager_, *common_.detector_,
            common_.query_extent_);
    bool placement_found = false;

    composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature, common_.vars_, src_over);
    double opacity = get<double>(sym, keys::opacity, feature, common_.vars_, 1.0);

    grid_text_renderer<T> ren(pixmap_,
                              comp_op,
                              common_.scale_factor_);

    placements_list const& placements = helper.get();
    value_integer feature_id = feature.id();
    
    for (glyph_positions_ptr glyphs : placements)
    {
        if (glyphs->marker())
        {
            render_marker(feature, 
                          pixmap_.get_resolution(),
                          glyphs->marker_pos(),
                          *(glyphs->marker()->marker),
                          glyphs->marker()->transform,
                          opacity, comp_op);
        }
        ren.render(*glyphs, feature_id);
        placement_found = true;
    }
    if (placement_found)
    {
        pixmap_.add_feature(feature);
    }
}
void grid_renderer<T>::process(polygon_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans)
{
    using renderer_type = agg::renderer_scanline_bin_solid<grid_renderer_base_type>;
    using pixfmt_type = typename grid_renderer_base_type::pixfmt_type;
    using color_type = typename grid_renderer_base_type::pixfmt_type::color_type;
    using vertex_converter_type = vertex_converter<transform2_tag,
                                                   clip_poly_tag,
                                                   transform_tag,
                                                   affine_transform_tag,
                                                   simplify_tag,
                                                   smooth_tag,
                                                   contour_tag>;

    ras_ptr->reset();

    grid_rendering_buffer buf(pixmap_.raw_data(), common_.width_, common_.height_, common_.width_);

    render_polygon_symbolizer<vertex_converter_type>(
      sym, feature, prj_trans, common_, common_.query_extent_, *ras_ptr,
      [&](color const &, double) {
        pixfmt_type pixf(buf);

        grid_renderer_base_type renb(pixf);
        renderer_type ren(renb);

        // render id
        ren.color(color_type(feature.id()));
        agg::scanline_bin sl;
        ras_ptr->filling_rule(agg::fill_non_zero);
        agg::render_scanlines(*ras_ptr, sl, ren);

        // add feature properties to grid cache
        pixmap_.add_feature(feature);
      });
}
Exemple #14
0
void grid_renderer<T>::render_marker(mapnik::feature_impl & feature, unsigned int step, pixel_position const& pos, marker const& marker, agg::trans_affine const& tr, double opacity, composite_mode_e comp_op)
{
    if (marker.is_vector())
    {
        typedef coord_transform<CoordTransform,geometry_type> path_type;
        typedef agg::renderer_base<mapnik::pixfmt_gray32> ren_base;
        typedef agg::renderer_scanline_bin_solid<ren_base> renderer;
        agg::scanline_bin sl;

        grid_rendering_buffer buf(pixmap_.raw_data(), width_, height_, width_);
        mapnik::pixfmt_gray32 pixf(buf);

        ren_base renb(pixf);
        renderer ren(renb);

        ras_ptr->reset();

        box2d<double> const& bbox = (*marker.get_vector_data())->bounding_box();
        coord<double,2> c = bbox.center();
        // center the svg marker on '0,0'
        agg::trans_affine mtx = agg::trans_affine_translation(-c.x,-c.y);
        // apply symbol transformation to get to map space
        mtx *= tr;
        mtx *= agg::trans_affine_scaling(scale_factor_*(1.0/step));
        // render the marker at the center of the marker box
        mtx.translate(pos.x, pos.y);
        using namespace mapnik::svg;
        vertex_stl_adapter<svg_path_storage> stl_storage((*marker.get_vector_data())->source());
        svg_path_adapter svg_path(stl_storage);
        svg_renderer_agg<svg_path_adapter,
            agg::pod_bvector<path_attributes>,
            renderer,
            mapnik::pixfmt_gray32> svg_renderer(svg_path,
                                                (*marker.get_vector_data())->attributes());

        svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), mtx, opacity, bbox);

    }
    else
    {
        image_data_32 const& data = **marker.get_bitmap_data();
        double width = data.width();
        double height = data.height();
        double cx = 0.5 * width;
        double cy = 0.5 * height;
        if (step == 1 && (std::fabs(1.0 - scale_factor_) < 0.001 && tr.is_identity()))
        {
            // TODO - support opacity
            pixmap_.set_rectangle(feature.id(), data,
                                  boost::math::iround(pos.x - cx),
                                  boost::math::iround(pos.y - cy));
        }
        else
        {
            // TODO - remove support for step != or add support for agg scaling with opacity
            double ratio = (1.0/step);
            image_data_32 target(ratio * data.width(), ratio * data.height());
            mapnik::scale_image_agg<image_data_32>(target,data, SCALING_NEAR,
                                                   scale_factor_, 0.0, 0.0, 1.0, ratio);
            pixmap_.set_rectangle(feature.id(), target,
                                  boost::math::iround(pos.x - cx),
                                  boost::math::iround(pos.y - cy));
        }
    }
    pixmap_.add_feature(feature);
}
void grid_renderer<T>::process(line_pattern_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans)
{
    std::string filename = get<std::string>(sym, keys::file, feature, common_.vars_);
    if (filename.empty()) return;
    boost::optional<mapnik::marker_ptr> mark = marker_cache::instance().find(filename, true);
    if (!mark) return;

    if (!(*mark)->is_bitmap())
    {
        MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Only images (not '" << filename << "') are supported in the line_pattern_symbolizer";
        return;
    }

    boost::optional<image_ptr> pat = (*mark)->get_bitmap_data();
    if (!pat) return;

    bool clip = get<value_bool>(sym, keys::clip, feature, common_.vars_, true);
    double offset = get<value_double>(sym, keys::offset, feature, common_.vars_, 0.0);
    double simplify_tolerance = get<value_double>(sym, keys::simplify_tolerance, feature, common_.vars_, 0.0);
    double smooth = get<value_double>(sym, keys::smooth, feature, common_.vars_, false);

    typedef typename grid_renderer_base_type::pixfmt_type pixfmt_type;
    typedef typename grid_renderer_base_type::pixfmt_type::color_type color_type;
    typedef agg::renderer_scanline_bin_solid<grid_renderer_base_type> renderer_type;
    typedef boost::mpl::vector<clip_line_tag, transform_tag,
                               offset_transform_tag, affine_transform_tag,
                               simplify_tag, smooth_tag, stroke_tag> conv_types;
    agg::scanline_bin sl;

    grid_rendering_buffer buf(pixmap_.raw_data(), common_.width_, common_.height_, common_.width_);
    pixfmt_type pixf(buf);

    grid_renderer_base_type renb(pixf);
    renderer_type ren(renb);

    ras_ptr->reset();

    int stroke_width = (*pat)->width();

    agg::trans_affine tr;
    auto transform = get_optional<transform_type>(sym, keys::geometry_transform);
    if (transform)
    {
        evaluate_transform(tr, feature, common_.vars_, *transform, common_.scale_factor_);
    }

    box2d<double> clipping_extent = common_.query_extent_;
    if (clip)
    {
        double padding = (double)(common_.query_extent_.width()/pixmap_.width());
        double half_stroke = stroke_width/2.0;
        if (half_stroke > 1)
            padding *= half_stroke;
        if (std::fabs(offset) > 0)
            padding *= std::fabs(offset) * 1.2;
        padding *= common_.scale_factor_;
        clipping_extent.pad(padding);
    }
    
    // to avoid the complexity of using an agg pattern filter instead
    // we create a line_symbolizer in order to fake the pattern
    line_symbolizer line;
    put<value_double>(line, keys::stroke_width, value_double(stroke_width));
    // TODO: really should pass the offset to the fake line too, but
    // this wasn't present in the previous version and makes the test
    // fail - in this case, probably the test should be updated.
    //put<value_double>(line, keys::offset, value_double(offset));
    put<value_double>(line, keys::simplify_tolerance, value_double(simplify_tolerance));
    put<value_double>(line, keys::smooth, value_double(smooth));

    vertex_converter<box2d<double>, grid_rasterizer, line_symbolizer,
                     CoordTransform, proj_transform, agg::trans_affine, conv_types, feature_impl>
        converter(clipping_extent,*ras_ptr,line,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_);
    if (clip) converter.set<clip_line_tag>(); // optional clip (default: true)
    converter.set<transform_tag>(); // always transform
    if (std::fabs(offset) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
    converter.set<affine_transform_tag>(); // optional affine transform
    if (simplify_tolerance > 0.0) converter.set<simplify_tag>(); // optional simplify converter
    if (smooth > 0.0) converter.set<smooth_tag>(); // optional smooth converter
    converter.set<stroke_tag>(); //always stroke

    for (geometry_type & geom : feature.paths())
    {
        if (geom.size() > 1)
        {
            converter.apply(geom);
        }
    }

    // render id
    ren.color(color_type(feature.id()));
    agg::render_scanlines(*ras_ptr, sl, ren);

    // add feature properties to grid cache
    pixmap_.add_feature(feature);

}
void grid_renderer<T>::process(line_pattern_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans)
{
    std::string filename = get<std::string, keys::file>(sym, feature, common_.vars_);
    if (filename.empty()) return;
    std::shared_ptr<mapnik::marker const> mark = marker_cache::instance().find(filename, true);
    if (mark->is<mapnik::marker_null>()) return;

    if (!mark->is<mapnik::marker_rgba8>())
    {
        MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Only images (not '" << filename << "') are supported in the line_pattern_symbolizer";
        return;
    }

    value_bool clip = get<value_bool, keys::clip>(sym, feature, common_.vars_);
    value_double offset = get<value_double, keys::offset>(sym, feature, common_.vars_);
    value_double simplify_tolerance = get<value_double, keys::simplify_tolerance>(sym, feature, common_.vars_);
    value_double smooth = get<value_double, keys::smooth>(sym, feature, common_.vars_);

    using pixfmt_type = typename grid_renderer_base_type::pixfmt_type;
    using color_type = typename grid_renderer_base_type::pixfmt_type::color_type;
    using renderer_type = agg::renderer_scanline_bin_solid<grid_renderer_base_type>;

    agg::scanline_bin sl;

    grid_rendering_buffer buf(pixmap_.raw_data(), common_.width_, common_.height_, common_.width_);
    pixfmt_type pixf(buf);

    grid_renderer_base_type renb(pixf);
    renderer_type ren(renb);

    ras_ptr->reset();

    line_pattern_enum pattern = get<line_pattern_enum, keys::line_pattern>(sym, feature, common_.vars_);
    std::size_t stroke_width = (pattern == LINE_PATTERN_WARP) ? mark->width() :
        get<value_double, keys::stroke_width>(sym, feature, common_.vars_);

    agg::trans_affine tr;
    auto transform = get_optional<transform_type>(sym, keys::geometry_transform);
    if (transform)
    {
        evaluate_transform(tr, feature, common_.vars_, *transform, common_.scale_factor_);
    }

    box2d<double> clipping_extent = common_.query_extent_;
    if (clip)
    {
        double pad_per_pixel = static_cast<double>(common_.query_extent_.width()/common_.width_);
        double pixels = std::ceil(std::max(stroke_width / 2.0 + std::fabs(offset),
                                          (std::fabs(offset) * offset_converter_default_threshold)));
        double padding = pad_per_pixel * pixels * common_.scale_factor_;

        clipping_extent.pad(padding);
    }

    // to avoid the complexity of using an agg pattern filter instead
    // we create a line_symbolizer in order to fake the pattern
    line_symbolizer line;
    put<value_double>(line, keys::stroke_width, value_double(stroke_width));
    // TODO: really should pass the offset to the fake line too, but
    // this wasn't present in the previous version and makes the test
    // fail - in this case, probably the test should be updated.
    //put<value_double>(line, keys::offset, value_double(offset));
    put<value_double>(line, keys::simplify_tolerance, value_double(simplify_tolerance));
    put<value_double>(line, keys::smooth, value_double(smooth));

    using vertex_converter_type = vertex_converter<clip_line_tag, transform_tag,
                                                   affine_transform_tag,
                                                   simplify_tag,smooth_tag,
                                                   offset_transform_tag,stroke_tag>;
    vertex_converter_type converter(clipping_extent,line,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_);
    if (clip) converter.set<clip_line_tag>();
    converter.set<transform_tag>(); // always transform
    if (std::fabs(offset) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
    converter.set<affine_transform_tag>(); // optional affine transform
    if (simplify_tolerance > 0.0) converter.set<simplify_tag>(); // optional simplify converter
    if (smooth > 0.0) converter.set<smooth_tag>(); // optional smooth converter
    converter.set<stroke_tag>(); //always stroke
    using apply_vertex_converter_type = detail::apply_vertex_converter<vertex_converter_type,grid_rasterizer>;
    using vertex_processor_type = geometry::vertex_processor<apply_vertex_converter_type>;
    apply_vertex_converter_type apply(converter, *ras_ptr);
    mapnik::util::apply_visitor(vertex_processor_type(apply),feature.get_geometry());

    // render id
    ren.color(color_type(feature.id()));
    agg::render_scanlines(*ras_ptr, sl, ren);

    // add feature properties to grid cache
    pixmap_.add_feature(feature);

}
void grid_renderer<T>::process(line_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans)
{
    using pixfmt_type = typename grid_renderer_base_type::pixfmt_type;
    using color_type = typename grid_renderer_base_type::pixfmt_type::color_type;
    using renderer_type = agg::renderer_scanline_bin_solid<grid_renderer_base_type>;
    using conv_types = boost::mpl::vector<clip_line_tag, transform_tag,
                                          offset_transform_tag, affine_transform_tag,
                                          simplify_tag, smooth_tag, dash_tag, stroke_tag>;
    agg::scanline_bin sl;

    grid_rendering_buffer buf(pixmap_.raw_data(), common_.width_, common_.height_, common_.width_);
    pixfmt_type pixf(buf);

    grid_renderer_base_type renb(pixf);
    renderer_type ren(renb);

    ras_ptr->reset();

    agg::trans_affine tr;
    auto transform = get_optional<transform_type>(sym, keys::geometry_transform);
    if (transform)
    {
        evaluate_transform(tr, feature, common_.vars_, *transform, common_.scale_factor_);
    }

    box2d<double> clipping_extent = common_.query_extent_;

    bool clip = get<value_bool>(sym, keys::clip, feature, common_.vars_, true);
    double width = get<value_double>(sym, keys::stroke_width, feature, common_.vars_,1.0);
    double offset = get<value_double>(sym, keys::offset, feature, common_.vars_,0.0);
    double simplify_tolerance = get<value_double>(sym, keys::simplify_tolerance, feature, common_.vars_,0.0);
    double smooth = get<value_double>(sym, keys::smooth, feature, common_.vars_,false);
    bool has_dash = has_key<dash_array>(sym, keys::stroke_dasharray);

    if (clip)
    {
        double padding = (double)(common_.query_extent_.width()/pixmap_.width());
        double half_stroke = width/2.0;
        if (half_stroke > 1)
            padding *= half_stroke;
        if (std::fabs(offset) > 0)
            padding *= std::fabs(offset) * 1.2;
        padding *= common_.scale_factor_;
        clipping_extent.pad(padding);
    }

    vertex_converter<box2d<double>, grid_rasterizer, line_symbolizer,
                     CoordTransform, proj_transform, agg::trans_affine, conv_types, feature_impl>
        converter(clipping_extent,*ras_ptr,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_);
    if (clip) converter.set<clip_line_tag>(); // optional clip (default: true)
    converter.set<transform_tag>(); // always transform
    if (std::fabs(offset) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
    converter.set<affine_transform_tag>(); // optional affine transform
    if (simplify_tolerance > 0.0) converter.set<simplify_tag>(); // optional simplify converter
    if (smooth > 0.0) converter.set<smooth_tag>(); // optional smooth converter
    if (has_dash) converter.set<dash_tag>();
    converter.set<stroke_tag>(); //always stroke

    for ( geometry_type & geom : feature.paths())
    {
        if (geom.size() > 1)
        {
            converter.apply(geom);
        }
    }

    // render id
    ren.color(color_type(feature.id()));
    ras_ptr->filling_rule(agg::fill_non_zero);
    agg::render_scanlines(*ras_ptr, sl, ren);

    // add feature properties to grid cache
    pixmap_.add_feature(feature);

}
void grid_renderer<T>::process(line_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans)
{
    using pixfmt_type = typename grid_renderer_base_type::pixfmt_type;
    using color_type = typename grid_renderer_base_type::pixfmt_type::color_type;
    using renderer_type = agg::renderer_scanline_bin_solid<grid_renderer_base_type>;

    agg::scanline_bin sl;

    grid_rendering_buffer buf(pixmap_.raw_data(), common_.width_, common_.height_, common_.width_);
    pixfmt_type pixf(buf);

    grid_renderer_base_type renb(pixf);
    renderer_type ren(renb);

    ras_ptr->reset();

    agg::trans_affine tr;
    auto transform = get_optional<transform_type>(sym, keys::geometry_transform);
    if (transform)
    {
        evaluate_transform(tr, feature, common_.vars_, *transform, common_.scale_factor_);
    }

    box2d<double> clipping_extent = common_.query_extent_;

    bool clip = get<value_bool>(sym, keys::clip, feature, common_.vars_, false);
    double width = get<value_double>(sym, keys::stroke_width, feature, common_.vars_,1.0);
    double offset = get<value_double>(sym, keys::offset, feature, common_.vars_,0.0);
    double simplify_tolerance = get<value_double>(sym, keys::simplify_tolerance, feature, common_.vars_,0.0);
    double smooth = get<value_double>(sym, keys::smooth, feature, common_.vars_,false);
    bool has_dash = has_key(sym, keys::stroke_dasharray);

    if (clip)
    {
        double padding = (double)(common_.query_extent_.width()/pixmap_.width());
        double half_stroke = width/2.0;
        if (half_stroke > 1)
            padding *= half_stroke;
        if (std::fabs(offset) > 0)
            padding *= std::fabs(offset) * 1.2;
        padding *= common_.scale_factor_;
        clipping_extent.pad(padding);
    }
    using vertex_converter_type = vertex_converter<clip_line_tag, clip_poly_tag, transform_tag,
                                                   affine_transform_tag,
                                                   simplify_tag, smooth_tag,
                                                   offset_transform_tag,
                                                   dash_tag, stroke_tag>;

    vertex_converter_type converter(clipping_extent,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_);
    if (clip)
    {
        geometry::geometry_types type = geometry::geometry_type(feature.get_geometry());
        if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon)
            converter.template set<clip_poly_tag>();
        else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString)
            converter.template set<clip_line_tag>();
    }
    converter.set<transform_tag>(); // always transform
    if (std::fabs(offset) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
    converter.set<affine_transform_tag>(); // optional affine transform
    if (simplify_tolerance > 0.0) converter.set<simplify_tag>(); // optional simplify converter
    if (smooth > 0.0) converter.set<smooth_tag>(); // optional smooth converter
    if (has_dash) converter.set<dash_tag>();
    converter.set<stroke_tag>(); //always stroke

    using apply_vertex_converter_type = detail::apply_vertex_converter<vertex_converter_type, grid_rasterizer>;
    using vertex_processor_type = geometry::vertex_processor<apply_vertex_converter_type>;
    apply_vertex_converter_type apply(converter, *ras_ptr);
    mapnik::util::apply_visitor(vertex_processor_type(apply),feature.get_geometry());

    // render id
    ren.color(color_type(feature.id()));
    ras_ptr->filling_rule(agg::fill_non_zero);
    agg::render_scanlines(*ras_ptr, sl, ren);

    // add feature properties to grid cache
    pixmap_.add_feature(feature);

}
void grid_renderer<T>::process(polygon_pattern_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans)
{
    std::string filename = get<std::string>(sym, keys::file, feature, common_.vars_);
    if (filename.empty()) return;
    boost::optional<mapnik::marker_ptr> mark = marker_cache::instance().find(filename, true);
    if (!mark) return;

    if (!(*mark)->is_bitmap())
    {
        MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Only images (not '" << filename << "') are supported in the line_pattern_symbolizer";
        return;
    }

    boost::optional<image_ptr> pat = (*mark)->get_bitmap_data();
    if (!pat) return;

    ras_ptr->reset();

    bool clip = get<value_bool>(sym, keys::clip, feature, common_.vars_, true);
    double simplify_tolerance = get<value_double>(sym, keys::simplify_tolerance, feature, common_.vars_, 0.0);
    double smooth = get<value_double>(sym, keys::smooth, feature, common_.vars_, false);

    agg::trans_affine tr;
    auto transform = get_optional<transform_type>(sym, keys::geometry_transform);
    if (transform)
    {
        evaluate_transform(tr, feature, common_.vars_, *transform, common_.scale_factor_);
    }

    using conv_types = boost::mpl::vector<clip_poly_tag,transform_tag,affine_transform_tag,smooth_tag>;
    vertex_converter<box2d<double>, grid_rasterizer, polygon_pattern_symbolizer,
                     CoordTransform, proj_transform, agg::trans_affine, conv_types, feature_impl>
        converter(common_.query_extent_,*ras_ptr,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_);

    if (prj_trans.equal() && clip) converter.set<clip_poly_tag>(); //optional clip (default: true)
    converter.set<transform_tag>(); //always transform
    converter.set<affine_transform_tag>();
    if (simplify_tolerance > 0.0) converter.set<simplify_tag>(); // optional simplify converter
    if (smooth > 0.0) converter.set<smooth_tag>(); // optional smooth converter

    for ( geometry_type & geom : feature.paths())
    {
        if (geom.size() > 2)
        {
            converter.apply(geom);
        }
    }
    using pixfmt_type = typename grid_renderer_base_type::pixfmt_type;
    using color_type = typename grid_renderer_base_type::pixfmt_type::color_type;
    using renderer_type = agg::renderer_scanline_bin_solid<grid_renderer_base_type>;

    grid_rendering_buffer buf(pixmap_.raw_data(), common_.width_, common_.height_, common_.width_);
    pixfmt_type pixf(buf);

    grid_renderer_base_type renb(pixf);
    renderer_type ren(renb);

    // render id
    ren.color(color_type(feature.id()));
    agg::scanline_bin sl;
    ras_ptr->filling_rule(agg::fill_even_odd);
    agg::render_scanlines(*ras_ptr, sl, ren);

    // add feature properties to grid cache
    pixmap_.add_feature(feature);
}
void grid_renderer<T>::process(polygon_pattern_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans)
{
    std::string filename = get<std::string, keys::file>(sym, feature, common_.vars_);
    if (filename.empty()) return;
    mapnik::marker const& mark = marker_cache::instance().find(filename, true);
    if (mark.is<mapnik::marker_null>()) return;

    if (!mark.is<mapnik::marker_rgba8>())
    {
        MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Only images (not '" << filename << "') are supported in the line_pattern_symbolizer";
        return;
    }

    ras_ptr->reset();

    value_bool clip = get<value_bool, keys::clip>(sym, feature, common_.vars_);
    value_double simplify_tolerance = get<value_double, keys::simplify_tolerance>(sym, feature, common_.vars_);
    value_double smooth = get<value_double, keys::smooth>(sym, feature, common_.vars_);

    agg::trans_affine tr;
    auto transform = get_optional<transform_type>(sym, keys::geometry_transform);
    if (transform)
    {
        evaluate_transform(tr, feature, common_.vars_, *transform, common_.scale_factor_);
    }

    using vertex_converter_type = vertex_converter<clip_poly_tag,transform_tag,affine_transform_tag,smooth_tag>;
    vertex_converter_type converter(common_.query_extent_,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_);

    if (prj_trans.equal() && clip) converter.set<clip_poly_tag>(); //optional clip (default: true)
    converter.set<transform_tag>(); //always transform
    converter.set<affine_transform_tag>();
    if (simplify_tolerance > 0.0) converter.set<simplify_tag>(); // optional simplify converter
    if (smooth > 0.0) converter.set<smooth_tag>(); // optional smooth converter

    using apply_vertex_converter_type = detail::apply_vertex_converter<vertex_converter_type, grid_rasterizer>;
    using vertex_processor_type = geometry::vertex_processor<apply_vertex_converter_type>;
    apply_vertex_converter_type apply(converter, *ras_ptr);
    mapnik::util::apply_visitor(vertex_processor_type(apply),feature.get_geometry());

    using pixfmt_type = typename grid_renderer_base_type::pixfmt_type;
    using color_type = typename grid_renderer_base_type::pixfmt_type::color_type;
    using renderer_type = agg::renderer_scanline_bin_solid<grid_renderer_base_type>;

    grid_rendering_buffer buf(pixmap_.raw_data(), common_.width_, common_.height_, common_.width_);
    pixfmt_type pixf(buf);

    grid_renderer_base_type renb(pixf);
    renderer_type ren(renb);

    // render id
    ren.color(color_type(feature.id()));
    agg::scanline_bin sl;
    ras_ptr->filling_rule(agg::fill_even_odd);
    agg::render_scanlines(*ras_ptr, sl, ren);

    // add feature properties to grid cache
    pixmap_.add_feature(feature);
}
void grid_renderer<T>::process(line_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans)
{
    typedef typename grid_renderer_base_type::pixfmt_type pixfmt_type;
    typedef typename grid_renderer_base_type::pixfmt_type::color_type color_type;
    typedef agg::renderer_scanline_bin_solid<grid_renderer_base_type> renderer_type;
    typedef boost::mpl::vector<clip_line_tag, transform_tag,
                               offset_transform_tag, affine_transform_tag,
                               simplify_tag, smooth_tag, dash_tag, stroke_tag> conv_types;
    agg::scanline_bin sl;

    grid_rendering_buffer buf(pixmap_.raw_data(), width_, height_, width_);
    pixfmt_type pixf(buf);

    grid_renderer_base_type renb(pixf);
    renderer_type ren(renb);

    ras_ptr->reset();

    stroke const& stroke_ = sym.get_stroke();

    agg::trans_affine tr;
    evaluate_transform(tr, feature, sym.get_transform(), scale_factor_);

    box2d<double> clipping_extent = query_extent_;
    if (sym.clip())
    {
        double padding = (double)(query_extent_.width()/pixmap_.width());
        double half_stroke = stroke_.get_width()/2.0;
        if (half_stroke > 1)
            padding *= half_stroke;
        if (std::fabs(sym.offset()) > 0)
            padding *= std::fabs(sym.offset()) * 1.2;
        padding *= scale_factor_;
        clipping_extent.pad(padding);
    }

    vertex_converter<box2d<double>, grid_rasterizer, line_symbolizer,
                     CoordTransform, proj_transform, agg::trans_affine, conv_types>
        converter(clipping_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);
    if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true)
    converter.set<transform_tag>(); // always transform
    if (std::fabs(sym.offset()) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
    converter.set<affine_transform_tag>(); // optional affine transform
    if (sym.simplify_tolerance() > 0.0) converter.set<simplify_tag>(); // optional simplify converter
    if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
    if (stroke_.has_dash()) converter.set<dash_tag>();
    converter.set<stroke_tag>(); //always stroke

    for ( geometry_type & geom : feature.paths())
    {
        if (geom.size() > 1)
        {
            converter.apply(geom);
        }
    }

    // render id
    ren.color(color_type(feature.id()));
    ras_ptr->filling_rule(agg::fill_non_zero);
    agg::render_scanlines(*ras_ptr, sl, ren);

    // add feature properties to grid cache
    pixmap_.add_feature(feature);

}
void grid_renderer<T>::process(line_pattern_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<marker_ptr> mark = marker_cache::instance().find(filename,true);
    if (!mark) return;

    if (!(*mark)->is_bitmap())
    {
        MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Only images (not '" << filename << "') are supported in the line_pattern_symbolizer";
        return;
    }

    boost::optional<image_ptr> pat = (*mark)->get_bitmap_data();
    if (!pat) return;

    typedef typename grid_renderer_base_type::pixfmt_type pixfmt_type;
    typedef typename grid_renderer_base_type::pixfmt_type::color_type color_type;
    typedef agg::renderer_scanline_bin_solid<grid_renderer_base_type> renderer_type;
    typedef boost::mpl::vector<clip_line_tag, transform_tag,
                               offset_transform_tag, affine_transform_tag,
                               simplify_tag, smooth_tag, stroke_tag> conv_types;
    agg::scanline_bin sl;

    grid_rendering_buffer buf(pixmap_.raw_data(), width_, height_, width_);
    pixfmt_type pixf(buf);

    grid_renderer_base_type renb(pixf);
    renderer_type ren(renb);

    ras_ptr->reset();

    int stroke_width = (*pat)->width();

    agg::trans_affine tr;
    evaluate_transform(tr, feature, sym.get_transform(), scale_factor_);

    box2d<double> clipping_extent = query_extent_;
    if (sym.clip())
    {
        double padding = (double)(query_extent_.width()/pixmap_.width());
        double half_stroke = stroke_width/2.0;
        if (half_stroke > 1)
            padding *= half_stroke;
        if (std::fabs(sym.offset()) > 0)
            padding *= std::fabs(sym.offset()) * 1.2;
        padding *= scale_factor_;
        clipping_extent.pad(padding);
    }
    
    // to avoid the complexity of using an agg pattern filter instead
    // we create a line_symbolizer in order to fake the pattern
    stroke str;
    str.set_width(stroke_width);
    line_symbolizer line(str);
    vertex_converter<box2d<double>, grid_rasterizer, line_symbolizer,
                     CoordTransform, proj_transform, agg::trans_affine, conv_types>
        converter(clipping_extent,*ras_ptr,line,t_,prj_trans,tr,scale_factor_);
    if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true)
    converter.set<transform_tag>(); // always transform
    if (std::fabs(sym.offset()) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
    converter.set<affine_transform_tag>(); // optional affine transform
    if (sym.simplify_tolerance() > 0.0) converter.set<simplify_tag>(); // optional simplify converter
    if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
    converter.set<stroke_tag>(); //always stroke

    for (geometry_type & geom : feature.paths())
    {
        if (geom.size() > 1)
        {
            converter.apply(geom);
        }
    }

    // render id
    ren.color(color_type(feature.id()));
    agg::render_scanlines(*ras_ptr, sl, ren);

    // add feature properties to grid cache
    pixmap_.add_feature(feature);

}