当前位置:首页 » 网页前端 » web瓦片贴图
扩展阅读
webinf下怎么引入js 2023-08-31 21:54:13
堡垒机怎么打开web 2023-08-31 21:54:11

web瓦片贴图

发布时间: 2023-02-24 08:07:37

㈠ 如何在Web上直接浏览大规模OSGB格式倾斜模型(二):转换OSGB

欢迎关注公号【 三维网格3D 】,第一时间获取最新文章

上一篇( 如何在Web上直接浏览大规模OSGB格式倾斜模型(一):解析OSGB )已经贴出了解析结果,让我们对OSGB倾斜模型文件内部结构有了比较清晰的认识,本篇我们将解析结果转成three.js对象,并实现单个模型的显示。

为什么转成three.js对象

从解析结果看,Osg对象的节点关系用three.js对象表达更容易,转换结果可以直接用three.js显示,通过我们团队的Cesium和three.js融合方案,可以轻松将成果用于Cesium开发的项目,既可以单独使用,又可以拥有GIS能力。

需要转换的Osg类型

解析得到的Osg对象,Type属性指示对象的类型,我们只是要转换倾斜摄影模型文件的Osg对象,而不是做一个通用的转换工具,所以先将类型做一下分类,只转换目前所用测试数据(100多G的测试数据)已知的类型。

PS:我们团队内部使用的版本,经过一年多的应用和改进,支持了更多类型的转换,填了许多坑,而且支持osgb、osgt、osg、osgjs四种格式文件,加载速度和健壮性都得到保障。欢迎咨询~

节点类

osg::Node

osg::Geode

osg::Group

osg::LOD

osg::PagedLOD

osg::MatrixTransform

几何类

osg::Geometry

osg::OsgArray

材质类

osg::StateSet

osg::Material

osg::StateAttribute

osg::Texture

osg::Texture2D

osg::Image

节点类主要考虑osg::MatrixTransform,因为这个节点主要的信息就是变换参数,会影响模型的位置、旋转、缩放等。

接下来我们着重介绍转换几何和材质这两类,LOD和PagedLOD参数不多,而且都是调度相关的,我们放在下一篇一起介绍。

转换几何对象

几何数据存储在Osg::Geometry,包含索引属性PrimitiveSetList、位置坐标属性VertexArray、纹理坐标属性TexCoordArray。我们将其转换为THREE.BufferGeometry。

varbufferGeometry=newTHREE.BufferGeometry();

顶点属性转换

顶点属性值是一个二维数组,我们直接调用flat方法,转为一维数组即可。

varpositions=newFloat32Array(osgGeometry.VertexArray.flat());

varuvs=newFloat32Array(osgGeometry.TexCoordArray.flat());

bufferGeometry.setAttribute('position',newTHREE.BufferAttribute(positions,3));

bufferGeometry.setAttribute('uv',newTHREE.BufferAttribute(uvs,2));

索引转换

索引类型有两种,有value指定,这里我们只对索引类型为4的情况(即绘图最小单元为四边形)进行特殊处理,其余的默认最小绘制单元为三角形。

varprimitiveSet=osgGeometry.PrimitiveSetList[0]

varindices=primitiveSet.data;

if(primitiveSet.value==4) {

letnewIndices=[];

for(leti=0; i<indices.length; i+=4) {

leti0=indices[i],

i1=indices[i+1],

i2=indices[i+2],

i3=indices[i+3];

newIndices.push(

i0, i1, i3,

i1, i2, i3

);

}

indices=newIndices;

}

bufferGeometry.setIndex(indices);

转换材质对象

倾斜摄影模型的材质最重要的就是贴图,作为原理介绍,我们不处理其他材质参数,直取贴图即可,当然工程应用上我们还是需要处理一些情况,保证其可用、可靠、健壮。

材质对象存储在Osg::Geometry的StateSet属性,该属性类型为Osg::StateSet,而我们要的贴图存储在Osg::StateSet的TextureAttributeList属性,一般就一张图。

倾斜模型已经包含了光影信息,为此我们将材质转为THREE.MeshBasicMaterial。

varmaterial=newTHREE.MeshBasicMaterial({

side:THREE.DoubleSide

});

处理贴图

贴图一般是jpg或者png格式,我们这里只处理这两种格式。需要注意的是图片数据为Uint8Array类型,我们需要先转为Blob,然后通过URL.createObjectURL创建url,最后使用THREE.TextureLoader加载。

varosgStateSet=osgGeometry.StateSet;

varosgImage=osgStateSet.TextureAttributeList[0].value.StateAttribute.Image

varfileName=osgImage.Name;

constisJPEG=fileName.search(/\.jpe?g($|\?)/i)>0

constisPNG=fileName.search(/\.png($|\?)/i)>0

if(!isPNG&&!isJPEG)return;

varmimeType=isPNG?'image/png':'image/jpeg';

varimageUri=newBlob([osgImage.Data], { type:mimeType });

imageUri=URL.createObjectURL(imageUri)

vartexture=newTHREE.TextureLoader().load(imageUri, ()=>{

texture.needsUpdate=true

})

创建网格

最后,使用转换好的几何体和材质创建模型网格。这里要注意,倾斜摄影模型是z轴朝上(z-up),而three.js渲染是y轴朝上(y-up),所以还需将网格绕x轴旋转y-90°才得到正常的渲染效果。

varmesh=newTHREE.Mesh(geometry, material);

mesh.rotation.x=-Math.PI/2

转换结果

显示效果

额,看起来并没有那么好看~毕竟这是单个瓦片最不清晰的那层,简化到不能看了。

下一篇,我们将主要介绍如果实现PagedLOD,来加载大规模倾斜摄影模型,敬请期待

欢迎关注公号【 三维网格3D 】,第一时间获取最新文章

㈡ WebGIS中的坐标系和瓦片地图

本文主要介绍坐标系和瓦片地图的相关知识, 他们是进行WebGIS开发的基础。

坐标系分为地理坐标系和投影坐标系,他们的定义如下:
地理坐标系 (Geographic Coordinate System):
    是使用三维球面来定义地球表面位置,以实现通过经纬度对地球表面点位引用的坐标系。包括角度测量单位、本初子午线和参考椭球体三部分。

投影坐标系 (Projection Coordinate System):
    是使用基于X,Y值的坐标系统来描述地球上某个点所处的位置。它由地理坐标系和投影方法两个要素所决定。

    地球表面是崎岖不平的,人们为了精确表示地球表面的位置,引入了 旋转椭球体 的概念。即用一个规则的旋转椭球体去逼近真实的地球表面。一个旋转椭球体的参数主要有以下三个:长半轴、短半轴、扁率。定义了这三个参数,也就唯一确定了一个旋转椭球体。

    定义了椭球体的形状后,还需要确定椭球体的位置。椭球体表面与真实地球表面存在差异,并且在世界的不同地区,这种差异也不尽相同。因此椭球体的定位直接决定了地理坐标与真实位置的误差。椭球体定位就是需要确定 大地基准面 ,从而确定椭球体与地球的相对位置。有以下两类大地基准面:

    确定了旋转椭球体的 形状 位置 ,那么地理坐标系的基础就确定了。接下来需要定义地球上任意一点的地理坐标表示方法。

    地理坐标,就是用经纬度表示地面点位的球面坐标。在大地测量学中,对于地理坐标系统中的经纬度有三种提法:天文经纬度、大地经纬度和地心经纬度。其中使用较多的是大地经纬度,其使用大地坐标(L,B,h)表示地面点在椭球面上的位置三个要素,他们的定义如下:

图示:

    这样就完成了地理坐标系的定义,地球上任意一点都能获得经纬度坐标了。

    在椭球面上表示的地球上物体的坐标,会给实际使用带来一些麻烦。更多的时候我们希望将地物展现在平面上,这时就需要引入投影坐标系的概念。

    在地球椭球面和平面之间建立点与点之间函数关系的数学方法,称为 地图投影
    地图投影的一般公式为:x = F(λ,φ), y = G(λ,φ)
    确定了投影方法后,也就确定了函数F和G,只要知道地面点的经纬度(λ,φ),便可以在投影平面上找到相对应的平面位置(x,y)。

投影方法有以下几类:

    以上两种方法都要进行分带投影。即按一定的间隔选取经线作为投影的中央经线,中央经线两侧一定范围内的地区按所选中央经线进行投影。这样做的目的是减小投影变形,方便在工程中使用。

具体的投影方法请点击小标题查看。

    选择一个地理坐标系,以及一个地图投影方法,就唯一确定了一个投影坐标系,从而可以使用平面坐标表示地球上物体的位置了。

    在Web地图领域,使用最为广泛的坐标系统就是 WGS84 Web Mercator 。谷歌地图、Virtual Earth、Bing Maps、网络地图、Mapabc、ArcGIS Online等都是采用这种坐标系。作为一个投影坐标系,需要两个基本的要素,一个是地理坐标系,还有一个是投影方法。我们分别来看:

    从名字可以看出,WGS84 Web Mercator坐标系采用的地理坐标系是WGS84坐标系,它属于地心坐标系,坐标系的原点位于地球质心,其基本参数如下:

    从名字上可以看出,WGS84 Web Mercator坐标系的投影方法和Mercator(墨卡托)投影有关,但是这个投影方法和不是标准的墨卡托投影。他们之间的区别在于,WGS84 Web Mercator在投影时将地球椭球当做圆球看待,这会导致本来是等角投影的墨卡托投影变得不再等角了,而是近似等角,也就是出现角度变形。

    以赤道为标准纬线,以本初子午线为中央经线,分别得到X轴和Y轴。两者的交点设为原点,规定纬度向北为正,向南为负;经度向东为正,向西为负。

对应于经纬度的范围就是:

    讨论坐标系不得不提到EPSG,EPSG的英文全称是European Petroleum Survey Group,中文名称为欧洲石油调查组织。这个组织成立于1986年,2005年并入IOGP(International Association of Oil & Gas Procers),中文名称为国际油气生产者协会。EPSG对几乎所有常用的坐标系统都进行了编号,统一了坐标系的表示,于是我们经常会看到使用EPSG编号来指代某一坐标系。

以下是几个常用坐标系的EPSG编号和单位:

    至于为何WGS84 Web Mercator有两个编号,这里面还是有一段故事的,可以去 这里 查看。

    查询全部的EPSG编号和详细信息请访问 EPSG官网 。

    互联网地图服务,常常通过采用构建瓦片地图的方式,加快用户的访问,减少数据传输量。具体而言,瓦片地图就是对投影后的地图在不同尺度(层)下进行切片,每个尺度得到的地图切片数量不同、表示范围不同、详细程度不同,但是图片的尺寸相同(一般为256*256),最终构成一个“瓦片金字塔“”。根据用户所浏览的区域范围,自动确定所要返回的切片层级,在满足用户查询需求的同时,保证了地图传输的效率。

    在投影坐标系的选择上,目前主流的地图服务提供商基本都选择的是WGS84 Web Mercator坐标系。但是在如何对投影后的地图进行切片并编号时,不同厂商之间存在较大的差异。

    以地图左上角为原点,X轴向右,Y轴向下,从0开始分别进行编号。Z的取值范围为[0, 18],在第z级别,x,y方向的瓦片个数均为:2 z 个,即x,y取值范围是[0 , 2 z -1]。

    WMTS较为特殊,WMTS中的TileMatrix对应于z,TileRow对应于y,TileCol对应于x。编号方式和谷歌与OSM相同。

    以地图左下角为原点,X轴向右,Y轴向上,从0开始分别进行编号。Z的编码规则与谷歌地图相同。

z=1时,这两种瓦片的编号如下图所示。

    微软Bing地图Z的编码规则与谷歌相同,同一层级的瓦片不用XY两个维度表示,而只用一个整数表示,该整数服从四叉树编码规则(QuadTree)。

    网络地图的瓦片定义的方式比较独特,原点的位置在经纬度都为0的地方,X向左为正,向右为负;Y向上为正,向下为负。切分的方式不像上述3种方法在每一级进行二等分,而是通过定义每一级的 地图分辨率 ,确定每一级应该划分的行列数。地图分辨率的表达式为:2 18-z ,其含义是每个像素所对应的实际长度。由此,可得每一级应该划分的行列数为:2πR/(256*2 18-z ),其中R为地球的半径,单位是米。

参考: https://blog.csdn.net/lxxlxx888/article/details/51897838

    本文记录了与WebGIS相关的坐标系和瓦片地图的知识,说明了他们直接的相互关系。希望WebGIS开发者有所帮助。

㈢ CAD/DWG图Web可视化一站式解决方案-唯杰地图-vjmap

DWG 图是 AutoCAD 是 私有 格式,只能在CAD软件上编辑查看,如何发布至Web上做数据展示,GIS分析应用开发,一直是业内头疼的事情。

传统的办法采用的解析AutoCAD图形绘制,并封装成 AcitveX 控件,在Web显示。但这需要Web端安装插件,并且只支持windows平台,而更重要的 Chrome 已不再支持ActiveX技术。

随着新技术的发展,html5技术已非常成熟,在渲染方面技术非常成熟,越来越多的方案采用html5渲染方式。但CAD与GIS在数据结构上存在较大的差异,CAD数据类型较为丰富,支持简单点、线、面、多义线、椭圆、块、文字等多种数据类型,而转换到GIS中,只转换为点、线、面、注记等类型。所以会导致存在着和真实CAD图形绘制差异的问题。

唯杰地图 https://vjmap.com 采用后端解析CAD的图形直接渲染成GIS瓦片数据;前端采用最新的WebGL技术,加载渲染出的栅格和矢量数据,完美解决上述问题;

唯杰地图 为用户自定义地图格式WebGIS可视化显示开发提供的一站式解决方案, 完全兼容dwg格式 、 23d效果完美切换 、 高性能webgl渲染 、 个性化地图定制 、 矢量栅格瓦片全支持 、 跨平台私有化部署 。支持的格式如常用的AutoCAD的DWG格式文件、GeoJSON等常用GIS文件格式,它使用WebGL矢量图块和自定义样式呈现交互式地图, 提供了全新的大数据可视化、实时流数据可视化功能,通过本产品可快速实现浏览器和移动端上美观、流畅的地图呈现与空间分析,可帮助您在网站中构建功能丰富、交互性强、可定制的地图应用。

通过 Service 对象的 openMap 方法可打开服务器上面已存在的地图,或者通过 fileid 传入 http 网络路径或服务器本地路径打开一幅 DWG 格式的 CAD图形 .

获取上面打开地图返回的 地理范围 建立 坐标系 ,然后创建 地图对象

当鼠标在地图的某个实体上面时,需要高亮显示或者点击查看,在 矢量瓦片 模式下可调用 ;在 栅格瓦片 下,可调用 enableLayerClickHighlight

地图提供了在地图之上绘制覆盖物的能力,比如点标记、纯文本标记、折线、圆、多边形、矩形等图形,在绘制中可以对点标记进行标题、图标等进行设置,对折线可以进行颜色、宽度等属性进行设置,对于面(圆、多边形、矩形)同样可以进行填充色、边框色、宽度等很多自定义属性进行设置。

增加一个marker覆盖物

创建很多正方形拉伸图层

效果如下:

demo地址 可访问 https://vjmap.com/demo/#/demo/map/overlay/fillextrusion/fillextrusion

㈣ 从零打造一个Web地图引擎

说到地图,大家一定很熟悉,平时应该都使用过网络地图、高德地图、腾讯地图等,如果涉及到地图相关的开发需求,也有很多选择,比如前面的几个地图都会提供一套 js API ,此外也有一些开源地图框架可以使用,比如 OpenLayers 、 Leaflet 等。

那么大家有没有想过这些地图是怎么渲染出来的呢,为什么根据一个经纬度就能显示对应的地图呢,不知道没关系,本文会带各位从零实现一个简单的地图引擎,来帮助大家了解 GIS 基础知识及 Web 地图的实现原理。

首先我们去高德地图上选个经纬度,作为我们后期的地图中心点,打开 高德坐标拾取 工具,随便选择一个点:

笔者选择了杭州的雷峰塔,经纬度为: [120.148732,30.231006] 。

地图瓦片我们使用高德的在线瓦片,地址如下:

目前各大地图厂商的瓦片服务遵循的规则是有不同的:

谷歌和 TMS 的瓦片区别可以通过该地址可视化的查看: 地图瓦片 。

虽然规范不同,但原理基本是一致的,都是把地球投影成一个巨大的正方形世界平面图,然后按照四叉树进行分层切割,比如第一层,只有一张瓦片,显示整个世界的信息,所以基本只能看到洲和海的名称和边界线,第二层,切割成四张瓦片,显示信息稍微多了一点,以此类推,就像一个金字塔一样,底层分辨率最高,显示的细节最多,瓦片数也最多,顶层分辨率最低,显示的信息很少,瓦片数量相对也最少:

每一层的瓦片数量计算公式:

十八层就需要 68719476736 张瓦片,所以一套地图瓦片整体数量是非常庞大的。

瓦片切好以后,通过行列号和缩放层级来保存,所以可以看到瓦片地址中有三个变量: x 、 y 、 z

通过这三个变量就可以定位到一张瓦片,比如下面这个地址,行号为 109280 ,列号为 53979 ,缩放层级为 17 :

对应的瓦片为:

关于瓦片的更多信息可以阅读 瓦片地图原理 。

高德地图使用的是 GCJ-02坐标系 ,也称火星坐标系,由中国国家测绘局在02年发布,是在GPS坐标( WGS-84 坐标系)基础上经加密后而来,也就是增加了非线性的偏移,让你摸不准真实位置,为了国家安全,国内地图服务商都需要使用 GCJ-02坐标系 。

WGS-84 坐标系是国际通用的标准, EPSG 编号为 EPSG:4326 ,通常GPS设备获取到的原始经纬度和国外的地图厂商使用的都是 WGS-84 坐标系。

这两种坐标系都是地理坐标系,球面坐标,单位为 度 ,这种坐标方便在地球上定位,但是不方便展示和进行面积距离计算,我们印象中的地图都是平面的,所以就有了另外一种平面坐标系,平面坐标系是通过投影的方式从地理坐标系中转换过来,所以也称为投影坐标系,通常单位为 米 ,投影坐标系根据投影方式的不同存在多种,在 Web 开发的场景里通常使用的是 Web墨卡托投影 ,编号为 EPSG:3857 ,它基于 墨卡托投影 ,把 WGS-84 坐标系投影成正方形:

这是通过舍弃了南北 85.051129纬度 以上的地区实现的,因为它是正方形,所以一个大的正方形可以很方便的被分割为更小的正方形。

坐标系更详细的信息可参考 GIS之坐标系统 , EPSG:3857 的详细信息可参考 EPSG:3857 。

上一节里我们简单介绍了一下坐标系,按照 Web 地图的标准,我们的地图引擎也选择支持 EPSG:3857 投影,但是我们通过高德工具获取到的是火星坐标系的经纬度坐标,所以第一步要把经纬度坐标转换为 Web墨卡托 投影坐标,这里为了简单,先直接把火星坐标当做 WGS-84 坐标,后面再来看这个问题。

转换方法网上一搜就有:

3857 坐标有了,它的单位是 米 ,那么怎么转换成瓦片的行列号呢,这就涉及到 分辨率 的概念了,即地图上一像素代表实际多少米,分辨率如果能从地图厂商的文档里获取是最好的,如果找不到,也可以简单计算一下(如果使用计算出来的也不行,那就只能求助搜索引擎了),我们知道地球半径是 6378137 米, 3857 坐标系把地球当做正圆球体来处理,所以可以算出地球周长,投影是贴着地球赤道的:

所以投影成正方形的世界平面图后的边长代表的就是地球的周长,前面我们也知道了每一层级的瓦片数量的计算方式,而一张瓦片的大小一般是 256*256 像素,所以用地球周长除以展开后的世界平面图的边长就知道了地图上每像素代表实际多少米:

地球周长算出来是 40075016.68557849 ,可以看到 OpenLayers 就是这么计算的:

3857 坐标的单位是 米 ,那么把坐标除以分辨率就可以得到对应的像素坐标,再除以 256 ,就可以得到瓦片的行列号:

函数如下:

接下来我们把层级固定为 17 ,那么分辨率 resolution 就是 1.194328566955879 ,雷峰塔的经纬度转成 3857 的坐标为: [13374895.665697495, 3533278.205310311] ,使用上面的函数计算出来行列号为: [43744, 11556] ,我们把这几个数据代入瓦片的地址里进行访问:

一片空白,这是为啥呢,其实是因为原点不一样, 4326 和 3857 坐标系的原点在赤道和本初子午线相交点,非洲边上的海里,而瓦片的原点在左上角:

再来看下图会更容易理解:

3857 坐标系的原点相当于在世界平面图的中间,向右为 x 轴正方向,向上为 y 轴正方向,而瓦片地图的原点在左上角,所以我们需要根据图上【绿色虚线】的距离计算出【橙色实线】的距离,这也很简单,水平坐标就是水平绿色虚线的长度加上世界平面图的一半,垂直坐标就是世界平面图的一半减去垂直绿色虚线的长度,世界平面图的一半也就是地球周长的一半,修改 getTileRowAndCol 函数:

这次计算出来的瓦片行列号为 [109280, 53979] ,代入瓦片地址:

结果如下:

可以看到雷峰塔出来了。

我们现在能根据一个经纬度找到对应的瓦片,但是这还不够,我们的目标是要能在浏览器上显示出来,这就需要解决两个问题,一个是加载多少块瓦片,二是计算每一块瓦片的显示位置。

渲染瓦片我们使用 canvas 画布,模板如下:

地图画布容器 map 的大小我们很容易获取:

地图中心点我们设在画布中间,另外中心点的经纬度 center 和缩放层级 zoom 因为都是我们自己设定的,所以也是已知的,那么我们可以计算出中心坐标对应的瓦片:

缩放层级还是设为 17 ,中心点还是使用雷峰塔的经纬度,那么对应的瓦片行列号前面我们已经计算过了,为 [109280, 53979] 。

中心坐标对应的瓦片行列号知道了,那么该瓦片左上角在世界平面图中的像素位置我们也就知道了:

计算出来为 [27975680, 13818624] 。这个坐标怎么转换到屏幕上呢,请看下图:

中心经纬度的瓦片我们计算出来了,瓦片左上角的像素坐标也知道了,然后我们再计算出中心经纬度本身对应的像素坐标,那么和瓦片左上角的差值就可以计算出来,最后我们把画布的原点移动到画布中间(画布默认原点为左上角,x轴正方向向右,y轴正方向向下),也就是把中心经纬度作为坐标原点,那么中心瓦片的显示位置就是这个差值。

补充一下将经纬度转换成像素的方法:

计算中心经纬度对应的像素坐标:

计算差值:

最后通过 canvas 来把中心瓦片渲染出来:

这里先来看看 getTileUrl 方法的实现:

这里随机了四个子域: webrd01 、 webrd02 、 webrd03 、 webrd04 ,这是因为浏览器对于同一域名同时请求的资源是有数量限制的,而当地图层级变大后需要加载的瓦片数量会比较多,那么均匀分散到各个子域下去请求可以更快的渲染出所有瓦片,减少排队等待时间,基本所有地图厂商的瓦片服务地址都支持多个子域。

为了方便看到中心点的位置,我们再额外渲染两条中心辅助线,效果如下:

可以看到中心点确实是雷峰塔,当然这只是渲染了中心瓦片,我们要的是瓦片铺满整个画布,对于其他瓦片我们都可以根据中心瓦片计算出来,比如中心瓦片左边的一块,它的计算如下:

所以我们只要计算出中心瓦片四个方向各需要几块瓦片,然后用一个双重循环即可计算出画布需要的所有瓦片,计算需要的瓦片数量很简单,请看下图:

画布宽高的一半减去中心瓦片占据的空间即可得到该方向剩余的空间,然后除以瓦片的尺寸就知道需要几块瓦片了:

我们把中心瓦片作为原点,坐标为 [0, 0] ,来个双重循环扫描一遍即可渲染出所有瓦片:

效果如下:

很完美。

拖动可以这么考虑,前面已经实现了渲染指定经纬度的瓦片,当我们按住进行拖动时,可以知道鼠标滑动的距离,然后把该距离,也就是像素转换成经纬度的数值,最后我们再更新当前中心点的经纬度,并清空画布,调用之前的方法重新渲染,不停重绘造成是在移动的视觉假象。

监听鼠标相关事件:

在 onMousemove 方法里计算拖动后的中心经纬度及重新渲染画布:

movementX 和 movementY 属性能获取本次和上一次鼠标事件中的移动值,兼容性不是很好,不过自己计算该值也很简单,详细请移步 MDN 。乘以当前分辨率把 像素 换算成 米 ,然后把当前中心点经纬度也转成 3857 的 米 坐标,偏移本次移动的距离,最后再转回 4326 的经纬度坐标作为更新后的中心点即可。

为什么 x 是减, y 是加呢,很简单,我们鼠标向右和向下移动时距离是正的,相应的地图会向右或向下移动, 4326 坐标系向右和向上为正方向,那么地图向右移动时,中心点显然是相对来说是向左移了,因为向右为正方向,所以中心点经度方向就是减少了,所以是减去移动的距离,而地图向下移动,中心点相对来说是向上移了,因为向上为正方向,所以中心点纬度方向就是增加了,所以加上移动的距离。

更新完中心经纬度,然后清空画布重新绘制:

效果如下:

可以看到已经凌乱了,这是为啥呢,其实是因为图片加载是一个异步的过程,我们鼠标移动过程中,会不断的计算出要加载的瓦片进行加载,但是可能上一批瓦片还没加载完成,鼠标已经移动到新的位置了,又计算出一批新的瓦片进行加载,此时上一批瓦片可能加载完成并渲染出来了,但是这些瓦片有些可能已经被移除画布,不需要显示,有些可能还在画布内,但是使用的还是之前的位置,渲染出来也是不对的,同时新的一批瓦片可能也加载完成并渲染出来,自然导致了最终显示的错乱。

知道原因就简单了,首先我们加个缓存对象,因为在拖动过程中,很多瓦片只是位置变了,不需要重新加载,同一个瓦片加载一次,后续只更新它的位置即可;另外再设置一个对象来记录当前画布上应该显示的瓦片,防止不应该出现的瓦片渲染出来:

因为需要记录瓦片的位置、加载状态等信息,我们创建一个瓦片类:

然后修改之前的双重循环渲染瓦片的逻辑:

效果如下:

可以看到,拖动已经正常了,当然,上述实现还是很粗糙的,需要优化的地方很多,比如:

1.一般会先排个序,优先加载中心瓦片

2.缓存的瓦片越来越多肯定也会影响性能,所以还需要一些清除策略

这些问题有兴趣的可以自行思考。

拖动是实时更新中心点经纬度,那么缩放自然更新缩放层级就行了:

效果如下:

功能是有了,不过效果很一般,因为我们平常使用的地图缩放都是有一个放大或缩小的过渡动画,而这个是直接空白然后重新渲染,不仔细看都不知道是放大还是缩小。

所以我们不妨加个过渡效果,当我们鼠标滚动后,先将画布放大或缩小,动画结束后再根据最终的缩放值来渲染需要的瓦片。

画布默认缩放值为 1 ,放大则在此基础上乘以 2 倍,缩小则除以 2 ,然后动画到目标值,动画期间设置画布的缩放值及清空画布,重新绘制画布上的已有瓦片,达到放大或缩小的视觉效果,动画结束后再调用 renderTiles 重新渲染最终缩放值需要的瓦片。

效果如下:

虽然效果还是一般,不过至少能看出来是在放大还是缩小。

前面还遗留了一个小问题,即我们把高德工具上选出的经纬度直接当做 4326 经纬度,前面也讲过,它们之间是存在偏移的,比如手机 GPS 获取到的经纬度一般都是 84 坐标,直接在高德地图显示,会发现和你实际位置不一样,所以就需要进行一个转换,有一些工具可以帮你做些事情,比如 Gcoord 、 coordtransform 等。

上述效果看着比较一般,其实只要在上面的基础上稍微加一点瓦片的淡出动画,效果就会好很多,目前一般都是使用 canvas 来渲染 2D 地图,如果自己实现动画不太方便,也有一些强大的 canvas 库可以选择,笔者最后使用 Konva.js 库重做了一版,加入了瓦片淡出动画,最终效果如下:

另外只要搞清楚各个地图的瓦片规则,就能稍加修改支持更多的地图瓦片:

具体实现限于篇幅不再展开,有兴趣的可以阅读本文源码。

本文详细的介绍了一个简单的 web 地图开发过程,上述实现原理仅是笔者的个人思路,不代表 openlayers 等框架的原理,因为笔者也是 GIS 的初学者,所以难免会有问题,或更好的实现,欢迎指出。

在线 demo : https://wanglin2.github.io/web_map_demo/

完整源码: https://github.com/wanglin2/web_map_demo