此地图瓦片抓取算法是根据Mapbarapi里的绘图算法写出来的,使用PHP编写,将代码保存为一个PHP文件后,然后使用命令行运行: $ php -f filename.php。
注:cmd>cd "x:\...\php.exe"。
<?php /** * 抓取mapbar切片的算法 by CrossYou at 2011/01/22 * * @version 1.0.0 * @link http://crossyou.cn/ */ $mapbarImgRoot = './mapbar/'; //每层地图切片的文件夹名称 $levelFolder = array('W','0','1','2','3','4','5','6','7','8','9','10','11','12','13','16'); //每层地图切片所跨的经度数 $cutImgLonRange=array(90,40,20,10,5,2,1,0.5,0.2,0.1,0.05,0.02,0.01,0.005,0.002,0.001); //每层地图切片所跨的维度数 $cutImgLatRange=array(90*0.8,40*0.8,20*0.8,10*0.8,5*0.8,2*0.8,0.8,0.5*0.8,0.2*0.8,0.1*0.8,0.05*0.8,0.02*0.8,0.01*0.8,0.005*0.8,0.002*0.8,0.001*0.8); $blockSize=array(10,10,10,10,10,10,10,10,10,10,50,50,50,50,50,50); for($zoomLevel=0;$zoomLevel<16;$zoomLevel++){ $clipXNum=ceil(360/$cutImgLonRange[$zoomLevel]); $clipYNum=ceil(90/$cutImgLatRange[$zoomLevel]); $clipLonRange = $cutImgLonRange[$zoomLevel]; $clipLatRange = $cutImgLatRange[$zoomLevel]; $clipXGap = -180-$clipLonRange/2; //从缩放级别5开始,只抓取中国范围内的地图,因为此级别以后的,非国内的都是背景图,抓了无意义。 if($zoomLevel>3){ //--------中国范围的地图 $west = 73; $east = 136; $north = 54; $south = 3; $chinaLonRange = 63;//中国经度跨度 $chinaLatRange = 51;//中国维度跨度 $chinaClipXNum = ceil($chinaLonRange/$clipLonRange); $chinaClipYNum = ceil($chinaLatRange/$clipLatRange); for($i=0;$i<$chinaClipXNum;$i++){ $lon = $west+$clipLonRange*$i; for($j=0;$j<$chinaClipYNum;$j++){ $lat = $south+$clipLatRange*$j; $clipXNo = floor($lon/$cutImgLonRange[$zoomLevel]); $clipYNo = floor($lat/$cutImgLatRange[$zoomLevel]); $clipXNo=($clipXNo)%$clipXNum; if($clipXNo>=$clipXNum/2)$clipXNo-=$clipXNum; if($clipXNo<-$clipXNum/2)$clipXNo+=$clipXNum; echo "\n新的切片... => \n"; echo '经纬度:'.$lon.' , '.$lat."\n"; echo "-------------------------------\n"; echo '切片序号:'.$clipXNo.' , '.$clipYNo."\n"; echo "-------------------------------\n"; //切片分文件夹存放位置 $folderXNo = (int)floor($clipXNo/$blockSize[$zoomLevel]); //切片分文件夹存放位置 $folderYNo = (int)floor($clipYNo/$blockSize[$zoomLevel]); if($folderXNo<0){ $folderXNo+=1; } if($folderYNo<0){ $folderYNo+=1; } $fileXNo =($clipXNo)-$folderXNo*$blockSize[$zoomLevel]; $fileYNo =($clipYNo)-$folderYNo*$blockSize[$zoomLevel]; $imgPre = 'http://img.mapbar.com/maplite/mapbank/mapbar/'; $imgDir = $levelFolder[$zoomLevel].'/'.$folderXNo.'_'.$folderYNo.'/'; $imgName = $fileXNo.'_'.$fileYNo.'.png'; $imgUrl = $imgPre.$imgDir.$imgName; $localImgDir = $mapbarImgRoot.$imgDir; $localImgName = $localImgDir.$imgName; if (!is_file($localImgName)){ createdir($localImgDir); if(copy($imgUrl,$localImgName)){ echo '图片:=> '.$imgDir.$imgName." 成功下载到本地\n\n"; } } } } }else{ for($i= -$clipXNum/2;$i<=$clipXNum/2;$i++){ $lon = $clipLonRange*$i; if($i<0)$lon+=$clipLonRange/2; if($i==0)continue; if($i>0)$lon-=$clipLonRange/2; for($j=-$clipYNum;$j<=$clipYNum;$j++){ $lat = $clipLatRange*$j; if($j<0)$lat+=$clipLatRange/2; if($j==0)continue; if($j>0)$lat-=$clipLatRange/2; $clipXNo = floor($lon/$cutImgLonRange[$zoomLevel]); $clipYNo = floor($lat/$cutImgLatRange[$zoomLevel]); $clipXNo=($clipXNo)%$clipXNum; if($clipXNo>=$clipXNum/2)$clipXNo-=$clipXNum; if($clipXNo<-$clipXNum/2)$clipXNo+=$clipXNum; echo "\n新的切片... => \n"; echo '经纬度:'.$lon.' , '.$lat."\n"; echo "-------------------------------\n"; echo '切片序号:'.$clipXNo.' , '.$clipYNo."\n"; echo "-------------------------------\n"; //切片分文件夹存放位置 $folderXNo = (int)floor($clipXNo/$blockSize[$zoomLevel]); //切片分文件夹存放位置 $folderYNo = (int)floor($clipYNo/$blockSize[$zoomLevel]); if($folderXNo<0){ $folderXNo+=1; } if($folderYNo<0){ $folderYNo+=1; } $fileXNo =($clipXNo)-$folderXNo*$blockSize[$zoomLevel]; $fileYNo =($clipYNo)-$folderYNo*$blockSize[$zoomLevel]; $imgPre = 'http://img.mapbar.com/maplite/mapbank/mapbar/'; $imgDir = $levelFolder[$zoomLevel].'/'.$folderXNo.'_'.$folderYNo.'/'; $imgName = $fileXNo.'_'.$fileYNo.'.png'; $imgUrl = $imgPre.$imgDir.$imgName; $localImgDir = $mapbarImgRoot.$imgDir; $localImgName = $localImgDir.$imgName; if (!is_file($localImgName)){ createdir($localImgDir); if(copy($imgUrl,$localImgName)){ echo '图片:=> '.$imgDir.$imgName." 成功下载到本地\n\n"; } } } } } } /** * 创建多级目录 摘自网络 * 如有好的建议,请到 http://crossyou.cn/给我留言 谢谢! * * @since 1.0.5 * @param string $dir */ function createdir($dir){ $array_dir=explode("/",$dir);//把多级目录分别放到数组中 $depth = count($array_dir); $path = ''; for($i=0;$i<$depth;$i++){ $path .= $array_dir[$i]."/"; if(!is_dir($path)){ mkdir($path); } } } ?>
作者制作的一个Demo:http://www.crossyou.cn/attachments/2011/03/mapbar.html。下面的源代码可能用到了一小点的jQuery。
var divId = 'map'; var imgWidth = 300; var imgHeight = 300; var imgExt = 'png'; var centerpoint = '120.15689,35.96333'; var point = centerpoint.split(','); var _centerLon = point[0]; var _centerLat = point[1]; var zoomlevel = 8; var $map = $('#'+divId); //每层地图切片的文件夹名称 var levelFolder = new Array('W','0','1','2','3','4','5','6','7','8','9','10','11','12','13','16'); //每层地图切片所跨的经度数 var cutImgLonRange = new Array(90,40,20,10,5,2,1,0.5,0.2,0.1,0.05,0.02,0.01,0.005,0.002,0.001); //每层地图切片所跨的维度数 var cutImgLatRange = new Array(90*0.8,40*0.8,20*0.8,10*0.8,5*0.8,2*0.8,0.8,0.5*0.8,0.2*0.8,0.1*0.8,0.05*0.8,0.02*0.8,0.01*0.8,0.005*0.8,0.002*0.8,0.001*0.8); //地图区块大小 将不同区块的放在不同的文件夹下面进行管理 var blockSize = new Array(10,10,10,10,10,10,10,10,10,10,50,50,50,50,50,50); //纬度的偏移 var latOffset = new Array(0,0,0,0,0,0,0,0,75,0,0,-150,0,0,0,0); function drawMap(){ var LatLon = coordOffsetDecrypt(_centerLon,_centerLat);//解密Mapbar坐标 centerLon = LatLon[0]; centerLat = LatLon[1]; var mapwidth = $map.width(); var mapheight = $map.height(); var halfNum5clipX = Math.ceil(mapwidth/imgWidth/2); var halfNum5clipY = Math.ceil(mapheight/imgHeight/2); var _blockSize = blockSize[zoomlevel]; var clipLonRange = cutImgLonRange[zoomlevel]; var clipLatRange = cutImgLatRange[zoomlevel]; var multiple = 100000; var clipXNum=(360/clipLonRange); rotationCosVal=1.0; rotationSinVal=0.0; var Clip = []; clipNo5X=Math.floor((centerLon)/clipLonRange); clipNo5Y=Math.floor((centerLat)/clipLatRange); if(clipNo5X<0)clipNo5X+=1; var mapX=mapwidth/2-Math.round(((centerLon*multiple)%(clipLonRange*multiple))*imgWidth/(clipLonRange*multiple)); if(centerLat>=0) { mapY=mapheight/2-imgHeight+Math.round(((centerLat*multiple)%(clipLatRange*multiple))*imgHeight/(clipLatRange*multiple)); }else { mapY=mapheight/2+Math.round(((centerLat*multiple)%(clipLatRange*multiple))*imgHeight/(clipLatRange*multiple)); } $map.html(''); // -- 真正给力的开始 for (var _clipXNo = -halfNum5clipX - 1; _clipXNo <= halfNum5clipX; _clipXNo++) { for (var _clipYNo = -halfNum5clipY - 1; _clipYNo <= halfNum5clipY; _clipYNo++) { try { var clipXNo = parseInt(clipNo5X + _clipXNo);//地图横向切片序列号 var clipYNo = parseInt(clipNo5Y + _clipYNo); clipXNo = (clipXNo) % clipXNum if (clipXNo >= (clipXNum / 2)) clipXNo -= clipXNum; if (clipXNo < (-clipXNum / 2)) clipXNo += clipXNum; var folderX = parseInt(Math.floor((clipXNo) / _blockSize)); var folderY = parseInt(Math.floor((clipYNo) / _blockSize)); if (folderX < 0) folderX += 1; if (folderY < 0) folderY += 1; var fileXNo = (clipXNo) - folderX * _blockSize; var fileYNo = (clipYNo) - folderY * _blockSize; var _strImgUrl = 'http://img.mapbar.com/maplite/mapbank/mapbar/' + levelFolder[zoomlevel] + '/'; if (zoomlevel >= 14) _strImgUrl += folderX + "/"; _strImgUrl += folderX + "_" + folderY + "/"; _strImgUrl += fileXNo + "_" + fileYNo + "." + imgExt; var clipLeft = (_clipXNo * imgWidth) + parseInt(mapX); var clipTop = (-(_clipYNo * imgHeight) + parseInt(mapY)); clipTop = clipTop + latOffset[zoomlevel]; var isClearImgUrl = false; if ((clipLeft < -imgWidth || clipLeft > mapwidth || clipTop > mapheight || clipTop < -imgHeight)) isClearImgUrl = true; if (isClearImgUrl) continue; var clipId = ((zoomlevel).toString(16) + (clipNo5X + _clipXNo).toString(16) + 'l' + (clipNo5Y + _clipYNo).toString(16)).toLowerCase(); if (_strImgUrl && _strImgUrl.indexOf("NaN") < 0) { if (Clip[clipId] == null) { Clip[clipId] = new Image(); Clip[clipId].id = clipId; Clip[clipId].name = clipId; Clip[clipId].unselectable = "on"; Clip[clipId].style.position = "absolute"; Clip[clipId].style.MozUserSelect = "none"; Clip[clipId].src = _strImgUrl; } var p2 = (clipLeft + imgWidth / 2 - mapwidth / 2) * rotationCosVal - (clipTop + imgHeight / 2 - mapheight / 2) * rotationSinVal + mapwidth / 2; var p5 = (clipLeft + imgWidth / 2 - mapwidth / 2) * rotationSinVal + (clipTop + imgHeight / 2 - mapheight / 2) * rotationCosVal + mapheight / 2; Clip[clipId].style.top = parseInt(p5 - imgHeight / 2) + "px"; Clip[clipId].style.left = parseInt(p2 - imgWidth / 2) + "px"; //var interval = parseInt($('#interval').val()); $map.append(Clip[clipId]); } _strImgUrl = null; } catch (e) { throw (e); } } } } // The follow is two helper functions /** * 将真实地理坐标加密为Mapbar经纬度坐标 * * @param x 经度值 * @param y 维度值 * @returns [x,y] */ function coordOffsetEncrypt(x,y){ x = parseFloat(x)*100000%36000000; y = parseFloat(y)*100000%36000000; _X = parseInt(((Math.cos(y/100000))*(x/18000))+((Math.sin(x/100000))*(y/9000))+x); _Y = parseInt(((Math.sin(y/100000))*(x/18000))+((Math.cos(x/100000))*(y/9000))+y); return [_X/100000.0,_Y/100000.0]; } /** * 将Mapbar经纬坐标解密为真实地理坐标 * * @param x 经度值 * @param y 维度值 * @returns [x,y] */ function coordOffsetDecrypt(x,y){ x = parseFloat(x)*100000%36000000; y = parseFloat(y)*100000%36000000; x1 = parseInt(-(((Math.cos(y/100000))*(x/18000))+((Math.sin(x/100000))*(y/9000)))+x); y1 = parseInt(-(((Math.sin(y/100000))*(x/18000))+((Math.cos(x/100000))*(y/9000)))+y); x2 = parseInt(-(((Math.cos(y1/100000))*(x1/18000))+((Math.sin(x1/100000))*(y1/9000)))+x+((x>0)?1:-1)); y2 = parseInt(-(((Math.sin(y1/100000))*(x1/18000))+((Math.cos(x1/100000))*(y1/9000)))+y+((y>0)?1:-1)); return [x2/100000.0,y2/100000.0]; }
原文出自 CrossYou'Blog :
抓取算法:http://www.crossyou.cn/an-algorithm-to-crawl-mapbar-map.htm
瓦片拼接算法:http://www.crossyou.cn/a-simple-map-of-the-source-code-mapbar.htm
注:以上内容仅供学习,在此严重感谢作者分享。
后记:经验证,百度使用的MapBar的地图,算法相同,其url前缀为:
http://img.mapbar.com/maplite/mapbank/baidu/