/***************************************************************************** * * This file is part of Mapnik (c++ mapping toolkit) * * Copyright (C) 2021 Artem Pavlenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ // mapnik #include #include #include #include MAPNIK_DISABLE_WARNING_PUSH #include #include "agg_ellipse.h" #include "agg_color_rgba.h" MAPNIK_DISABLE_WARNING_POP namespace mapnik { void build_ellipse(symbolizer_base const& sym, mapnik::feature_impl & feature, attributes const& vars, svg_storage_type & marker_ellipse, svg::svg_path_adapter & svg_path) { double width = 0.0; double height = 0.0; double half_stroke_width = 0.0; if (has_key(sym,keys::width) && has_key(sym,keys::height)) { width = get(sym, keys::width, feature, vars, 0.0); height = get(sym, keys::height, feature, vars, 0.0); } else if (has_key(sym,keys::width)) { width = height = get(sym, keys::width, feature, vars, 0.0); } else if (has_key(sym,keys::height)) { width = height = get(sym, keys::height, feature, vars, 0.0); } if (has_key(sym,keys::stroke_width)) { half_stroke_width = get(sym, keys::stroke_width, feature, vars, 0.0) / 2.0; } svg::svg_converter_type styled_svg(svg_path, marker_ellipse.attributes()); styled_svg.push_attr(); styled_svg.begin_path(); agg::ellipse c(0, 0, width/2.0, height/2.0); styled_svg.storage().concat_path(c); styled_svg.end_path(); styled_svg.pop_attr(); double lox,loy,hix,hiy; styled_svg.bounding_rect(&lox, &loy, &hix, &hiy); lox -= half_stroke_width; loy -= half_stroke_width; hix += half_stroke_width; hiy += half_stroke_width; styled_svg.set_dimensions(width,height); marker_ellipse.set_dimensions(width,height); marker_ellipse.set_bounding_box(lox,loy,hix,hiy); } bool push_explicit_style(svg_attribute_type const& src, svg_attribute_type & dst, symbolizer_base const& sym, feature_impl & feature, attributes const& vars) { auto fill_color = get_optional(sym, keys::fill, feature, vars); auto fill_opacity = get_optional(sym, keys::fill_opacity, feature, vars); auto stroke_color = get_optional(sym, keys::stroke, feature, vars); auto stroke_width = get_optional(sym, keys::stroke_width, feature, vars); auto stroke_opacity = get_optional(sym, keys::stroke_opacity, feature, vars); if (fill_color || fill_opacity || stroke_color || stroke_width || stroke_opacity) { bool success = false; for(unsigned i = 0; i < src.size(); ++i) { dst.push_back(src[i]); mapnik::svg::path_attributes & attr = dst.back(); if (!attr.visibility_flag) continue; success = true; if (!attr.stroke_none) { if (stroke_width) { attr.stroke_width = *stroke_width; attr.stroke_flag = true; } if (stroke_color) { color const& s_color = *stroke_color; attr.stroke_color = agg::rgba(s_color.red()/255.0, s_color.green()/255.0, s_color.blue()/255.0, s_color.alpha()/255.0); attr.stroke_flag = true; } if (stroke_opacity) { attr.stroke_opacity = *stroke_opacity; attr.stroke_flag = true; } } if (!attr.fill_none) { if (fill_color) { color const& f_color = *fill_color; attr.fill_color = agg::rgba(f_color.red()/255.0, f_color.green()/255.0, f_color.blue()/255.0, f_color.alpha()/255.0); attr.fill_flag = true; } if (fill_opacity) { attr.fill_opacity = *fill_opacity; attr.fill_flag = true; } } } return success; } return false; } void setup_transform_scaling(agg::trans_affine & tr, double svg_width, double svg_height, mapnik::feature_impl & feature, attributes const& vars, symbolizer_base const& sym) { double width = get(sym, keys::width, feature, vars, 0.0); double height = get(sym, keys::height, feature, vars, 0.0); if (width > 0 && height > 0) { double sx = width/svg_width; double sy = height/svg_height; tr *= agg::trans_affine_scaling(sx,sy); } else if (width > 0) { double sx = width/svg_width; tr *= agg::trans_affine_scaling(sx); } else if (height > 0) { double sy = height/svg_height; tr *= agg::trans_affine_scaling(sy); } } template void apply_markers_single(vertex_converter_type & converter, Processor & proc, geometry::geometry const& geom, geometry::geometry_types type) { if (type == geometry::geometry_types::Point) { geometry::point_vertex_adapter va(geom.get>()); converter.apply(va, proc); } else if (type == geometry::geometry_types::LineString) { geometry::line_string_vertex_adapter va(geom.get>()); converter.apply(va, proc); } else if (type == geometry::geometry_types::Polygon) { geometry::polygon_vertex_adapter va(geom.get>()); converter.apply(va, proc); } else if (type == geometry::geometry_types::MultiPoint) { for (auto const& pt : geom.get>()) { geometry::point_vertex_adapter va(pt); converter.apply(va, proc); } } else if (type == geometry::geometry_types::MultiLineString) { for (auto const& line : geom.get>()) { geometry::line_string_vertex_adapter va(line); converter.apply(va, proc); } } else if (type == geometry::geometry_types::MultiPolygon) { for (auto const& poly : geom.get>()) { geometry::polygon_vertex_adapter va(poly); converter.apply(va, proc); } } } template void apply_markers_multi(feature_impl const& feature, attributes const& vars, vertex_converter_type & converter, Processor & proc, symbolizer_base const& sym) { 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_markers_single(converter, proc, geom, type); } else { marker_multi_policy_enum multi_policy = get(sym, feature, vars); marker_placement_enum placement = get(sym, feature, vars); if (placement == MARKER_POINT_PLACEMENT && multi_policy == MARKER_WHOLE_MULTI) { geometry::point 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(); geometry::point_vertex_adapter va(pt); converter.apply(va, proc); } } else if ((placement == MARKER_POINT_PLACEMENT || placement == MARKER_INTERIOR_PLACEMENT || placement == MARKER_POLYLABEL_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 const& multi_poly = mapnik::util::get >(geom); double maxarea = 0; geometry::polygon const* largest = 0; for (geometry::polygon const& poly : multi_poly) { box2d bbox = geometry::envelope(poly); double area = bbox.width() * bbox.height(); if (area > maxarea) { maxarea = area; largest = &poly; } } if (largest) { geometry::polygon_vertex_adapter va(*largest); converter.apply(va, proc); } } 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'"; } if (type == geometry::geometry_types::GeometryCollection) { for (auto const& g : geom.get>()) { apply_markers_single(converter, proc, g, geometry::geometry_type(g)); } } else { apply_markers_single(converter, proc, geom, type); } } } } template void apply_markers_multi(feature_impl const& feature, attributes const& vars, vertex_converter_type & converter, vector_dispatch_type & proc, symbolizer_base const& sym); template void apply_markers_multi(feature_impl const& feature, attributes const& vars, vertex_converter_type & converter, raster_dispatch_type & proc, symbolizer_base const& sym); } // end namespace mapnik