mapnik切图和地图分辨率与比例尺
mapbox 瓦片相关: 地址
osm瓦片相关: 地址
bingMAP瓦片相关: 地址
#include <mapnik/map.hpp> #include <mapnik/load_map.hpp> #include <mapnik/agg_renderer.hpp> #include <mapnik/version.hpp> #include <mapnik/debug.hpp> #include <mapnik/image_util.hpp> #include <mapnik/unicode.hpp> #include <mapnik/datasource_cache.hpp> #include <mapnik/font_engine_freetype.hpp> #include <mapnik/projection.hpp> #include <mapnik/proj_transform.hpp> #include <math.h> #pragma GCC diagnostic push #include <mapnik/warning_ignore.hpp> #include <boost/algorithm/string.hpp> #include <boost/program_options.hpp> #pragma GCC diagnostic pop #include <stdlib.h> #include <string> #include <sstream> //转换经纬度为瓦片左上角经纬度 //左上为0,0原点的 xyz转wgs84 googl瓦片(目前使用) //左下为0,0原点的 xyz TMS瓦片,需要反转Y轴,再使用 //赤道与0度经线为0,0原点的 xyz 百度瓦片。百度的图片像素坐标起点为左下角(目前不支持) //墨卡托坐标系:展开地球,赤道作为x轴,向东为x轴正方,本初子午线作为y轴,向北为y轴正方向。 //魔卡托 地球半径 单位m static const double EARTH_LENGTH = 6378137.0; //魔卡托 地球的周长(赤道)的一半 20037508.342789244 单位米 周长2*pi*R 墨卡托坐标x轴区间[-20037508.3427892,20037508.3427892] static const double circumferenceHalf = M_PI * 2 * EARTH_LENGTH / 2.0; //图片像素 static const int img_pixel=256; //屏幕dpi static const int screen_dpi=96; //(tileSize * pow(2, zoom) map width = map height = 256 * 2 ^level pixels zoom级别时 一张瓦片代表地图的像素数 /*** * 地图比例尺resolution * @param zoom z方向层级 从0级开始 * @param tileSize 图片尺寸 * @return 地图分辨率 */ double z2resolution (double zoom, int tileSize = 256) { // 米/每像素 auto resolution = M_PI * 2 * EARTH_LENGTH / (tileSize * pow(2, zoom)); return resolution; } /* * 图像分辨率表示的单位inch所包含的像素点数,即PPI(Pixels Per Inch)或者DPI(Dots Per Inch)。一般地图默认DPI为96。 也有用图元来描述屏幕的可分辨率,如wmts1.0.0中像元大小为0.00028m来界定,这样1inch = 0.0254m 0.0254/0.00028 ≈ 90.71。 */ /*** * 根据地图分辨率计算比例尺 * @param resolution 地图分辨率 * @param dpi DPI PPI屏幕分辨率 每英寸 mapnik默认ppi 90.71 google 72 * @return 比例尺 */ double resolution2scale(double resolution,double dpi=96) { return 0.0254/(resolution*dpi); } /*** * 比例尺转比例尺分母 * @param scale 比例尺 * @return 比例尺分母 */ double scale2scaleDenominator(double scale) { return 1.0/scale; } int long2tilex(double lon, int z) { return (int)(floor((lon + 180.0) / 360.0 * pow(2.0,z))); } int lat2tiley(double lat, int z) { double latrad = lat * M_PI/180.0; return (int)(floor((1.0 - asinh(tan(latrad)) / M_PI) / 2.0 * pow(2.0,z))); } double tilex2long(int x, int z) { return x / pow(2.0,z) * 360.0 - 180; } double tiley2lat(int y, int z) { double n = M_PI - (2.0 * M_PI * y )/ pow(2.0,z); return 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); } /** * 反转y轴,用于将左上原点转换到左下原点 * @param y 左上原点Y值 * @param z 层级 * @return 左下原点Y值 */ int tiley2reversal(int y,int z) { int ext = (int) pow(2, z); int changeRow = ext - y - 1; return changeRow; } /*** * 计算z代表的经纬度范围 * @param z xyz层级0-n * @return 当前层级瓦片的width 单位deg */ double zlevel2tilewidth(int z) { int ext = (int)pow(2,z); double tilewidth = 360.0/ext; return tilewidth; } /*** * 将xyz转换为瓦片经纬度bobox * @param x * @param y * @param z * @return */ mapnik::box2d<double> xyz2box(int x,int y,int z) { auto min_lng = tilex2long(x,z); auto min_lat = tiley2lat(y+1,z); auto max_lng = tilex2long(x+1,z); auto max_lat = tiley2lat(y,z); mapnik::box2d<double> boundingBox; boundingBox.set_minx(min_lng); boundingBox.set_maxy(max_lat); boundingBox.set_miny(min_lat); boundingBox.set_maxx(max_lng); return boundingBox; } int main (int argc,char** argv) { // if(argc != 4) // return -1; // int x=strtol(argv[0], nullptr,10); // int y=strtol(argv[1], nullptr,10); // int z=strtol(argv[2], nullptr,10); // auto xml_path=argv[3]; namespace po = boost::program_options; bool verbose = false; bool auto_open = false; int return_value = 0; int x=0,y=0,z=0; std::string xml_file; std::string img_file; double scale_factor = 1; bool params_as_variables = false; auto to = mapnik::projection("+init=epsg:3857"); auto from = mapnik::projection("+init=epsg:4326"); mapnik::proj_transform tr(from,to); mapnik::logger logger; logger.set_severity(mapnik::logger::error); try { po::options_description desc("mapnik-render utility"); 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<std::string>(),"xml map to read") ("img",po::value<std::string>(),"image to render") ("scale-factor",po::value<double>(),"scale factor for rendering") ("variables","make map parameters available as render-time variables") ("x",po::value<int>(),"xyz") ("y",po::value<int>(),"xyz") ("z",po::value<int>(),"xyz") ; po::positional_options_description p; p.add("xml",1); p.add("img",1); p.add("x",1); p.add("y",1); p.add("z",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<std::string>(); std::clog << "xmlPath:" << xml_file << std::endl; } else { std::clog << "please provide an xml map as first argument!" << std::endl; return -1; } if (vm.count("img")) { img_file=vm["img"].as<std::string>(); std::clog << "img_file:" << img_file << std::endl; } 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<double>(); } if (vm.count("variables")) { params_as_variables = true; } if (vm.count("x")) { x = vm["x"].as<int>(); std::clog << "x:" << x << std::endl; } if (vm.count("y")) { y = vm["y"].as<int>(); std::clog << "y:" << y << std::endl; } if (vm.count("z")) { z = vm["z"].as<int>(); std::clog << "z:" << z << std::endl; } auto m_resolution = z2resolution(z,256); auto m_scale = resolution2scale(m_resolution,90.72); auto m_scaleDenominator = scale2scaleDenominator(m_scale); std::ostringstream out; out.precision(15); out << std::fixed << m_resolution; std::cout << "z level:===>"<<z<< std::endl; std::cout << "pie==>m_resolution:===>" << out.str()<< std::endl; out.str(""); out <<m_scale; std::cout << "pie==>m_scale:===>" << out.str() << std::endl; out.str(""); out <<m_scaleDenominator; std::cout << "pie==>m_scaleDenominator:===>" << out.str() << std::endl; out.str(""); //mapnik::datasource_cache::instance().register_datasources("./plugins/input/"); mapnik::freetype_engine::register_fonts("./fonts", true); mapnik::Map map(256,256,"+init=epsg:3857"); mapnik::load_map(map,xml_file); // std::clog << "map.width" << map.width()<< std::endl; // std::clog << "map.height" << map.height()<< std::endl; //map.set_aspect_fix_mode(mapnik::Map::aspect_fix_mode::ADJUST_BBOX_HEIGHT); auto box = xyz2box(x,y,z); // std::clog << "xyz2box:minx" << box.minx() << std::endl; // std::clog << "xyz2box:miny" << box.miny() << std::endl; // std::clog << "xyz2box:maxx" << box.maxx() << std::endl; // std::clog << "xyz2box:maxy" << box.maxy() << std::endl; bool isok = tr.forward(box); // std::clog << "xyz2box:minx" << box.minx() << std::endl; // std::clog << "xyz2box:miny" << box.miny() << std::endl; // std::clog << "xyz2box:maxx" << box.maxx() << std::endl; // std::clog << "xyz2box:maxy" << box.maxy() << std::endl; map.zoom_to_box(box); out << map.scale_denominator(); std::clog << "mapnik:scale_denominator===>" << out.str() << std::endl; out.str(""); out << 1.0/map.scale_denominator(); std::clog << "mapnik:scale===>" << out.str() << std::endl; out.str(""); mapnik::image_rgba8 im(img_pixel,img_pixel); auto current_extent = map.get_current_extent(); mapnik::request req(img_pixel,img_pixel,box); // std::clog << "get_current_extent:minx" << current_extent.minx() << std::endl; // std::clog << "get_current_extent:miny" << current_extent.miny() << std::endl; // std::clog << "get_current_extent:maxx" << current_extent.maxx() << std::endl; // std::clog << "get_current_extent:maxy" << current_extent.maxy() << std::endl; // std::clog << "buffer_size" << map.buffer_size() << std::endl; req.set_buffer_size(2); 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<mapnik::value_integer>()) { vars[name] = param.second.get<mapnik::value_integer>(); } else if (param.second.is<mapnik::value_double>()) { vars[name] = param.second.get<mapnik::value_double>(); } else if (param.second.is<std::string>()) { vars[name] = tr.transcode(param.second.get<std::string>().c_str()); } } } } mapnik::agg_renderer<mapnik::image_rgba8> ren(map,req,vars,im); ren.apply(); std::clog << "begin save_to_file" << std::endl; std::string pngstr; std::ostringstream s; pngstr = mapnik::save_to_string(im,"png8"); mapnik::save_to_stream(im,s,"png8"); mapnik::save_to_file(im,img_file); // std::clog << "string:=========>" << std::endl; // std::clog << pngstr << std::endl; // std::clog << "stream:=========>" << std::endl; // std::clog << s.str() << std::endl; std::clog << "end save_to_file" << std::endl; 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; }