#include #include #include #include #include #include #include #include #include #include #include #include MAPNIK_DISABLE_WARNING_PUSH #include #include #include #include #include MAPNIK_DISABLE_WARNING_POP #include #if __cplusplus >= 201703L #include namespace fs = std::filesystem; #else #include namespace fs = std::experimental::filesystem; #endif BOOST_FUSION_ADAPT_STRUCT(mapnik::box2d, (double, minx_) (double, miny_) (double, maxx_) (double, maxy_)) int main(int argc, char** argv) { namespace po = boost::program_options; mapnik::setup(); bool verbose = false; bool auto_open = false; int return_value = 0; std::string xml_file; std::string img_file; double scale_factor = 1; bool params_as_variables = false; mapnik::logger logger; logger.set_severity(mapnik::logger::error); int map_width = 600; int map_height = 400; try { po::options_description desc("mapnik-render utility"); // clang-format off desc.add_options() ("help,h", "produce usage message") ("version,V","print version string") ("verbose,v","verbose output") ("open","automatically open the file after rendering") ("xml",po::value(),"xml map to read") ("img",po::value(),"image to render") ("scale-factor",po::value(),"scale factor for rendering") ("map-width",po::value(),"map width in pixels") ("map-height",po::value(),"map height in pixels") ("variables","make map parameters available as render-time variables") ("bbox", po::value(), "bounding box e.g in Map's SRS") ("geographic,g","bounding box is in WGS 84 lon/lat") ("plugins-dir", po::value(), "directory containing input plug-ins (default: ./plugins/input)") ("fonts-dir", po::value(), "directory containing fonts (default: relative to or ./fonts if no specified)"); // clang-format on po::positional_options_description p; p.add("xml", 1); p.add("img", 1); po::variables_map vm; po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm); po::notify(vm); if (vm.count("version")) { std::clog << "version " << MAPNIK_VERSION_STRING << std::endl; return 1; } if (vm.count("help")) { std::clog << desc << std::endl; return 1; } if (vm.count("verbose")) { verbose = true; } if (vm.count("open")) { auto_open = true; } if (vm.count("xml")) { xml_file = vm["xml"].as(); } else { std::clog << "please provide an xml map as first argument!" << std::endl; return -1; } if (vm.count("img")) { img_file = vm["img"].as(); } else { std::clog << "please provide an img as second argument!" << std::endl; return -1; } if (vm.count("scale-factor")) { scale_factor = vm["scale-factor"].as(); } if (vm.count("variables")) { params_as_variables = true; } if (vm.count("map-width")) { map_width = vm["map-width"].as(); } if (vm.count("map-height")) { map_height = vm["map-height"].as(); } if (vm.count("plugins-dir")) { mapnik::datasource_cache::instance().register_datasources(vm["plugins-dir"].as()); if (vm.count("fonts-dir")) { mapnik::freetype_engine::register_fonts(vm["fonts-dir"].as(), true); } else { // relative to plugins-dir try { fs::path p(vm["plugins-dir"].as()); p = p.parent_path() / "fonts"; mapnik::freetype_engine::register_fonts(p, true); } catch (...) {} } } else { mapnik::datasource_cache::instance().register_datasources("./plugins/input"); mapnik::freetype_engine::register_fonts("./fonts", true); } if (verbose) { auto plugin_names = mapnik::datasource_cache::instance().plugin_names(); if (plugin_names.empty()) { std::cerr << "*WARNING*: no datasource plug-ings registered" << std::endl; } else { std::cerr << "Registered datasource plug-ins:"; for (auto const& name : plugin_names) { std::cerr << name << " "; } std::cerr << std::endl; } } mapnik::Map map(map_width, map_height); mapnik::load_map(map, xml_file, true); if (vm.count("bbox")) { namespace x3 = boost::spirit::x3; mapnik::box2d bbox; std::string str = vm["bbox"].as(); auto start = str.begin(); auto end = str.end(); if (!x3::phrase_parse(start, end, x3::double_ >> -x3::lit(',') >> x3::double_ >> -x3::lit(',') >> x3::double_ >> -x3::lit(',') >> x3::double_, x3::space, bbox)) { std::cerr << "Failed to parse BBOX: " << str << std::endl; return -1; } if (!bbox.valid()) { std::cerr << "Invalid BBOX: " << str << std::endl; return -1; } if (vm.count("geographic")) { mapnik::projection source("epsg:4326"); mapnik::projection destination(map.srs()); mapnik::proj_transform tr(source, destination); if (!tr.forward(bbox)) { std::cerr << "Failed to project input BBOX into " << map.srs() << std::endl; return -1; } } std::cerr << "zoom to:" << bbox << std::endl; map.zoom_to_box(bbox); } else { map.zoom_all(); } mapnik::image_rgba8 im(map.width(), map.height()); mapnik::request req(map.width(), map.height(), map.get_current_extent()); req.set_buffer_size(map.buffer_size()); mapnik::attributes vars; if (params_as_variables) { mapnik::transcoder tr("utf-8"); for (auto const& param : map.get_extra_parameters()) { std::string const& name = param.first.substr(1); if (!name.empty()) { if (param.second.is()) { vars[name] = param.second.get(); } else if (param.second.is()) { vars[name] = param.second.get(); } else if (param.second.is()) { vars[name] = tr.transcode(param.second.get().c_str()); } } } } mapnik::agg_renderer ren(map, req, vars, im, scale_factor, 0, 0); ren.apply(); mapnik::save_to_file(im, img_file); if (auto_open) { std::ostringstream s; #ifdef __APPLE__ s << "open "; #elif _WIN32 s << "start "; #else s << "xdg-open "; #endif s << img_file; int ret = system(s.str().c_str()); if (ret != 0) return_value = ret; } else { std::clog << "rendered to: " << img_file << "\n"; } } catch (std::exception const& ex) { std::clog << "Error " << ex.what() << std::endl; return -1; } return return_value; }