让OpenLayers添加百度地图(未完版)
来自:http://www.cnblogs.com/laoyu/archive/2012/06/10/2544379.html
让OpenLayers添加百度地图(未完版)
KoalaGIS的在线地图系统,打算采用国内外主要的几个地图服务:google地图、百度地图、OpenStreetMap、天地图。google、百度、openstreet的服务器性能好,速度快,但google由于政策原因,不确定因素太多,OpenstreetMap个人感觉配图不怎么好看,添加天地图完全就是为了顾忌下咱们这个GIS专业的脸面,毕竟总要搞点专业的东西吧,虽然他很垃圾,但胜于无!
为此,需要在OpenLayers上扩展一个百度地图图层出来,让其能在Openlayers中显示出来。为了叠加百度地图,首先必须了解其瓦片的组织规则,通过对百度api的js文件解析和网上相关资料的收集,经过程序测试,推论正确,现讲解下其基本的参数,以及编写的KoalaGIS.Layer.WMTS.Baidu图层。
百度地图也是采用的魔卡托投影,个人感觉和google的web墨卡托投影是一样的,但据网友测试,两者计算结果有点出入,我猜测可能是百度或者google地图有偏移参数的影响吧,该文暂不分析此原因,且该问题本人还没正式测试过,有待考证!
利用百度提供的API进行坐标转换
varprojection=newBMap.MercatorProjection();
varpoint=projection.lngLatToPoint(newBMap.Point(116.404,39.915));
得出的结果是12958175,4825923.77
用Openlayers提供的web墨卡托投影对该经纬度投影得出的结果为:????
百度地图的分辨率问题
对百度地图api的js脚本进行解析,可以看到该函数:
可以看到两个获取分辨率相关的函数如下:
getZoomUnits:function(T){
returnMath.pow(2,(18-T))//从这里可以看出,百度地图的分辨率几乎就是固定的2的N次幂来计算的
},
详细请见该文《百度地图的瓦片参数解析》http://blog.sina.com.cn/s/blog_4c8b1bdd0100xij4.html
百度地图的坐标系统:平面坐标系的原点与经纬度的原点一致,即赤道与0度经线相交的位置
百度地图瓦片编号方式:
百度地图API在展示地图时是将整个地图图片切割成若干图块来显示的,当地图初始化或是地图级别、中心点位置发生变化时,地图API会根据当前像素坐标计算出视野内需要的图块坐标(也叫图块编号),从而加载对应的图块用以显示地图。
百度地图的图块坐标原点与平面坐标一致,从原点向右上方开始编号为0,0:
如何通过一个坐标和缩放级别计算出百度瓦片所在的行列号呢?
百度瓦片的计算方式很简单,大致如下:
1.计算出坐标的平面坐标位置,瓦片的切割起止点就是0,0
2.用平面坐标除以分辨率就能得到坐标到起点的像素大小,然后除以每张瓦片的大小取整就是瓦片的行列号了
cx=[((x-0)/res)/tileWidth]
cy=[((y-0)/res)/tileHeight]
分辨率前面已经讲过,res=Math.Pow(2,level-18);
3.举例来说吧,第18级下,选取经纬度为(116.404,39.915)的坐标点,该点大致在北京天,安,门那,经过投影得出其平面位置为(12958175,4825923.77)采用百度的api计算出来的,用openlayers计算的不是该坐标。利用上面的行列号计算公式推算出瓦片行列号为:50617,18851。利用百度地图提供的瓦片服务器地址,根据行列号和缩放级别,可以调用该瓦片地址:
OpenLayers中添加百度地图
遇到的问题:
1,坐标系不一致。
Openlayers中采用的坐标系并非百度地图的数学坐标系
2,行列号的计算方式不一样
百度地图是从左到右,从下到上;但Openlayers和google以及通常的wmts服务都是从左到右,从上到下的!
解决办法:坐标系还是按照Openlayers的思路,所有的方式都不动,只是把他计算瓦片的方式改变就OK了。
KoalaGIS.Layer.WMTS.Baidu.js文件
代码如下:
(function(){
window.KoalaGIS={
VERSION:'1.0.0',
Author:'KolaGISStudio'
};
KoalaGIS.Layer={};
KoalaGIS.Layer.WMTS={};
})();
KoalaGIS.Layer.WMTS.Baidu=OpenLayers.Class(OpenLayers.Layer.XYZ,{
url:null,
tileOrigin:newOpenLayers.LonLat(-20037508.34,20037508.34),
tileSize:newOpenLayers.Size(256,256),
type:'png',
useScales:false,
overrideDPI:false,
initialize:function(options){
this.resolutions=[];
for(vari=0;i<19;i++){
this.resolutions[i]=Math.pow(2,18-i);
}
OpenLayers.Layer.XYZ.prototype.initialize.apply(this,arguments);
if(this.resolutions){
this.serverResolutions=this.resolutions;
this.maxExtent=this.getMaxExtentForResolution(this.resolutions[0]);
}
//thisblockstepsthroughtranslatingthevaluesfromtheserverlayerJSON
//capabilitiesobjectintovaluesthatwecanuse.Thisisalsoahelpful
//referencewhenconfiguringthislayerdirectly.
if(this.layerInfo){
//aliastheobject
varinfo=this.layerInfo;
//buildourextents
varstartingTileExtent=newOpenLayers.Bounds(
info.fullExtent.xmin,
info.fullExtent.ymin,
info.fullExtent.xmax,
info.fullExtent.ymax
);
//setourprojectionbasedonthegivenspatialreference.
//esriusesslightlydifferentIDs,sothismaynotbecomprehensive
this.projection='EPSG:'+info.spatialReference.wkid;
this.sphericalMercator=(info.spatialReference.wkid==102100);
//convertesriunitsintoopenlayersunits(basicfeetormetersonly)
this.units=(info.units=="esriFeet")?'ft':'m';
//optionalextendedsectionbasedonwhetherornottheserverreturned
//specifictileinformation
if(!!info.tileInfo){
//eithersetthetilesbasedonrows/columns,orspecificwidth/height
this.tileSize=newOpenLayers.Size(
info.tileInfo.width||info.tileInfo.cols,
info.tileInfo.height||info.tileInfo.rows
);
//thismustbesetwhenmanuallyconfiguringthislayer
this.tileOrigin=newOpenLayers.LonLat(
info.tileInfo.origin.x,
info.tileInfo.origin.y
);
varupperLeft=newOpenLayers.Geometry.Point(
startingTileExtent.left,
startingTileExtent.top
);
varbottomRight=newOpenLayers.Geometry.Point(
startingTileExtent.right,
startingTileExtent.bottom
);
if(this.useScales){
this.scales=[];
}else{
this.resolutions=[];
}
this.lods=[];
for(varkeyininfo.tileInfo.lods){
if(info.tileInfo.lods.hasOwnProperty(key)){
varlod=info.tileInfo.lods[key];
if(this.useScales){
this.scales.push(lod.scale);
}else{
this.resolutions.push(lod.resolution);
}
varstart=this.getContainingTileCoords(upperLeft,lod.resolution);
lod.startTileCol=start.x;
lod.startTileRow=start.y;
varend=this.getContainingTileCoords(bottomRight,lod.resolution);
lod.endTileCol=end.x;
lod.endTileRow=end.y;
this.lods.push(lod);
}
}
this.maxExtent=this.calculateMaxExtentWithLOD(this.lods[0]);
this.serverResolutions=this.resolutions;
if(this.overrideDPI&&info.tileInfo.dpi){
//seecommentabovefor'overrideDPI'
OpenLayers.DOTS_PER_INCH=info.tileInfo.dpi;
}
}
}
},
getContainingTileCoords:function(point,res){
//returnnewOpenLayers.Pixel(
//Math.max(Math.floor((point.x-this.tileOrigin.lon)/(this.tileSize.w*res)),0),
//Math.max(Math.floor((this.tileOrigin.lat-point.y)/(this.tileSize.h*res)),0)
//);
returnnewOpenLayers.Pixel(
Math.floor((point.x-this.tileOrigin.lon)/(this.tileSize.w*res)),
Math.floor((point.y-this.tileOrigin.lat)/(this.tileSize.h*res))
);
},
calculateMaxExtentWithLOD:function(lod){
//themaxextentwe'reprovidedwithjustoverlapssometiles
//ourrealextentistheboundsofallthetileswetouch
varnumTileCols=(lod.endTileCol-lod.startTileCol)+1;
varnumTileRows=(lod.endTileRow-lod.startTileRow)+1;
varminX=this.tileOrigin.lon+(lod.startTileCol*this.tileSize.w*lod.resolution);
varmaxX=minX+(numTileCols*this.tileSize.w*lod.resolution);
varmaxY=this.tileOrigin.lat-(lod.startTileRow*this.tileSize.h*lod.resolution);
varminY=maxY-(numTileRows*this.tileSize.h*lod.resolution);
returnnewOpenLayers.Bounds(minX,minY,maxX,maxY);
},
calculateMaxExtentWithExtent:function(extent,res){
varupperLeft=newOpenLayers.Geometry.Point(extent.left,extent.top);
varbottomRight=newOpenLayers.Geometry.Point(extent.right,extent.bottom);
varstart=this.getContainingTileCoords(upperLeft,res);
varend=this.getContainingTileCoords(bottomRight,res);
varlod={
resolution:res,
startTileCol:start.x,
startTileRow:start.y,
endTileCol:end.x,
endTileRow:end.y
};
returnthis.calculateMaxExtentWithLOD(lod);
},
getUpperLeftTileCoord:function(res){
varupperLeft=newOpenLayers.Geometry.Point(
this.maxExtent.left,
this.maxExtent.top);
returnthis.getContainingTileCoords(upperLeft,res);
},
getLowerRightTileCoord:function(res){
varbottomRight=newOpenLayers.Geometry.Point(
this.maxExtent.right,
this.maxExtent.bottom);
returnthis.getContainingTileCoords(bottomRight,res);
},
getMaxExtentForResolution:function(res){
varstart=this.getUpperLeftTileCoord(res);
varend=this.getLowerRightTileCoord(res);
varnumTileCols=(end.x-start.x)+1;
//varnumTileRows=(end.y-start.y)+1;
varnumTileRows=(start.y-end.y)+1;
varminX=this.tileOrigin.lon+(start.x*this.tileSize.w*res);
varmaxX=minX+(numTileCols*this.tileSize.w*res);
//varmaxY=this.tileOrigin.lat-(start.y*this.tileSize.h*res);
varmaxY=this.tileOrigin.lat+(start.y*this.tileSize.h*res);
varminY=maxY-(numTileRows*this.tileSize.h*res);
returnnewOpenLayers.Bounds(minX,minY,maxX,maxY);
},
clone:function(obj){
if(obj==null){
obj=newOpenLayers.Layer.ArcGISCache(this.name,this.url,this.options);
}
returnOpenLayers.Layer.XYZ.prototype.clone.apply(this,[obj]);
},
getMaxExtent:function(){
varresolution=this.map.getResolution();
returnthis.maxExtent=this.getMaxExtentForResolution(resolution);
},
getTileOrigin:function(){
//debugger;
varextent=this.getMaxExtent();
returnnewOpenLayers.LonLat(extent.left,extent.bottom);
},
getURL:function(bounds){
//debugger;
varz=this.map.getZoom();
varres=this.getResolution();
//z=18-z;
//varres=Math.pow(2,z-18);
//tilecenter
varoriginTileX=(this.tileOrigin.lon+(res*this.tileSize.w/2));
//varoriginTileY=(this.tileOrigin.lat-(res*this.tileSize.h/2));
varoriginTileY=(this.tileOrigin.lat+(res*this.tileSize.h/2));
originTileX=0;
originTileY=0;
varcenter=bounds.getCenterLonLat();
//center.lat=4825923.77;
//center.lon=12958175;
varpoint={x:center.lon,y:center.lat};
//varx=(Math.round(Math.abs((center.lon-originTileX)/(res*this.tileSize.w))));
////vary=(Math.round(Math.abs((originTileY-center.lat)/(res*this.tileSize.h))));
//vary=(Math.round(Math.abs((center.lat-originTileY)/(res*this.tileSize.h))));
varx=(Math.round((center.lon-originTileX)/(res*this.tileSize.w)));
//vary=(Math.round(Math.abs((originTileY-center.lat)/(res*this.tileSize.h))));
vary=(Math.round((center.lat-originTileY)/(res*this.tileSize.h)));
//x=Math.round(center.lon*1/this.tileSize.w);
//y=Math.round(center.lat*1/this.tileSize.h);
//varx=Math.floor(Math.abs((center.lon)*res/this.tileSize.w));
//vary=(Math.round(Math.abs((originTileY-center.lat)/(res*this.tileSize.h))));
//vary=Math.floor(Math.abs((center.lat)*res/this.tileSize.h));
//x=Math.round(Math.abs(x)/256);
//y=Math.round(Math.abs(y)/256);
//thispreventsusfromgettingpinktiles(non-existanttiles)
if(this.lods){
varlod=this.lods[this.map.getZoom()];
if((x<lod.startTileCol||x>lod.endTileCol)
||(y<lod.startTileRow||y>lod.endTileRow)){
returnnull;
}
}
else{
varstart=this.getUpperLeftTileCoord(res);
varend=this.getLowerRightTileCoord(res);
//if((x<start.x||x>=end.x)
//||(y<start.y||y>=end.y)){
//returnnull;
//}
if((x<start.x||x>=end.x)
||(y>=start.y||y<end.y)){
//returnnull;
}
}
//Constructtheurlstring
varurl=this.url;
vars=''+x+y+z;
if(OpenLayers.Util.isArray(url)){
url=this.selectUrl(s,url);
}
//AccessingtilesthroughArcGISServerusesadifferentpath
//structurethandirectaccessviathefolderstructure.
if(this.useArcGISServer){
//AGSMapServershaveprettyurlaccesstotiles
url=url+'/tile/z/{y}/{x}';}else{//Thetileimagesarestoredusinghexvaluesondisk.//x='C'+this.zeroPad(x,8,16);//y='R'+this.zeroPad(y,8,16);//z='L'+this.zeroPad(z,2,16);//url=url+'/{z}/y/{x}.'+this.type;
varx_str='x′;varystr=′{y}';
if(x<0)
x_str='Mx′;if(y<0)ystr=′M{y}';
url=url+'/u=x='+x_str+';y='+y_str+';z=${z};v=011;type=web&fm=44';
}
//Writethevaluesintoourformattedurl
url=OpenLayers.String.format(url,{'x':Math.abs(x),'y':Math.abs(y),'z':z});
returnurl;
},
zeroPad:function(num,len,radix){
varstr=num.toString(radix||10);
while(str.length<len){
str="0"+str;
}
returnstr;
},
CLASS_NAME:'KoalaGIS.Layer.WMTS.Baidu'
});