WebGIS中的坐标系和瓦片地图
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WebGIS中的坐标系和瓦片地图相关的知识,希望对你有一定的参考价值。
参考技术A本文主要介绍坐标系和瓦片地图的相关知识, 他们是进行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 Producers),中文名称为国际油气生产者协会。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开发者有所帮助。
WebGIS中通过行列号来换算出多种瓦片的URL 之离线地图(转载)
WebGIS中通过行列号来换算出多种瓦片的URL 之离线地图
1.前言
在前面我花了两个篇幅来讲解行列号的获取,也解释了为什么要获取行列号。在这一章,我将把常见的几种请求瓦片时的URL样式罗列出来,并且给出大致的解释。
我在这里将地图分为离线地图和在线地图。所谓离线地图,即保存在本地而没有发布的地图。在线地图即发布与网上,可以通过浏览器访问的地图。
2.ArcGIS切图——exploded类型
在前面章节中我已经贴出了exploded类型的切图图片,这里再次给出。
那么如何通过行列号来换算出此瓦片的URL呢。我们首先可以通过观察得出三个结论:
(1)L开头的代表了Level,R开头的代表了row,C开头的代表了Col。
(2)确定这个后,我们再继续观察,可以发现L后的数字是两位字符串,R后的是八位字符串,C后的也是八位字符串。
(3)英文后的数字均是16进制数,然后不足位数的用0补充。
我想大家在知道了这三个结论后,通过行列号来获得离线松散瓦片的地址该不难了吧,我们只需把级别、行列号换算成16进制后,不足位数的再用0补位,最后加上英文标识,于是这个瓦片的地址也便可以额找到了。
3.ArcGIS 切图——bundle类型
这里我也首先贴出这种瓦片类型的样式:
这个瓦片的获取咋一看确实是毫无头绪,因为arcgis的这种紧凑型格式将图片进行了包装,并不能直观的看到图片。ArcGIS号称这种格式目前是不公开解析方法的,并且同样在网上也很难收到对应的解析方法。但是,我的一个很有想法的同事,在去年时花了些时间后已经将这种格式下的瓦片获取方法破解了,并且我们已经成功运用到多个项目中。
这里我就只给出几个提示吧,根据我的这几个提示,我想读者只要再加一把劲一定可以破解的:
(1)同样,L、R、C后的是地图的级别、行号、列号。
(2)R、C后的字符串固定是4位。
(3)R、C后的数字是通过行列号除以128后再转成16进制,然后将不足的位数补零。
(4)Bundle文件中存放的是图片二进制流,BundleX文件中存放的是对应瓦片在Bundle中的地址,是一个索引文件。
(5)然后…..
然后就是如何在索引文件中找到应该读取的地方,获得瓦片在Bundle中所在的地址后,再去Bundle中的相应地址里读取图片。不过,这里补充一下,Bundle中也不是只有瓦片的,它里面还包含了每个瓦片的大小,也就是你读这个瓦片需要读取多少个二进制的数目。
这里再次感谢我的同事的智慧的结晶,否则这篇文章一定是不完整的。
4.非常见瓦片格式——国土局的瓦片
在我们项目中经常可以见到非ArcGIS的瓦片系列,比如超图的、中地的等等。这里我给出某国土局的瓦片格式,其实目前很多国土局自己的瓦片均是这个组织格式。
同样我先给出瓦片的样式图:
大家是不是很奇怪,明明该是三个层次的呀,Level、Row、Col的呀,怎么这个就有四个层次呢。是的,国土局的瓦片中除了这三个参数外,还有一个FileID参数。
这里我直接给出换算公式:
FixedLevel=Level;
FixedRow=Math.floor(Row/4);
FixedCol=Math.floor(Col/4);
FileID=(Row)%4)+ 4*((Col)%4);
其URL的地址就是\FixedLevel\FixedRow\FixedCol\FileID.png。
5.总结
在这一节里我们针对两种常见离线地图格式和一种特殊的离线地图格式进行了解析。从这个解析中我们可以看出,不管是什么地图,行列号都是必须的条件。所以对行列号如何得到还不是很清楚的读者,请将我这个系列中的第二节和第三节再次专心的读一遍,相信你和我一样一定有不一样的收获的。
下一节里,我们将对在线的地图的URL解析进行讲解,在线的地图的URL获取相对简单。我们同样会对符合OGC标准的在线地图以及特殊的在线地图服务进行分析。欢迎持续关注。
ArcGIS Flex API读取自定义瓦片地图
PortlandTiledMapServiceLayer.as
package com.esri.ags.samples { import com.esri.ags.SpatialReference; import com.esri.ags.geometry.Extent; import com.esri.ags.geometry.MapPoint; import com.esri.ags.layers.supportClasses.LOD; import com.esri.ags.layers.supportClasses.TileInfo; import com.esri.ags.layers.TiledMapServiceLayer; import flash.net.URLRequest; /** * PortlandTiledMapServiceLayer */ public class PortlandTiledMapServiceLayer extends TiledMapServiceLayer { //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Creates a new PortlandTiledMapServiceLayer object. */ public function PortlandTiledMapServiceLayer() { super(); buildTileInfo(); // to create our hardcoded tileInfo setLoaded(true); // Map will only use loaded layers } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- private var _tileInfo:TileInfo = new TileInfo(); // see buildTileInfo() private var _baseURL:String = "http://sampleserver1.arcgisonline.com/arcgiscache/Portland_Portland_ESRI_LandBase_AGO/Portland/_alllayers"; //-------------------------------------------------------------------------- // // Overridden properties // fullExtent() // initialExtent() // spatialReference() // tileInfo() // units() // //-------------------------------------------------------------------------- //---------------------------------- // fullExtent // - required to calculate the tiles to use //---------------------------------- override public function get fullExtent():Extent { return new Extent(-123.596895130725, 44.297575737946, -121.553757125519, 46.3683237161949, new SpatialReference(4326)); } //---------------------------------- // initialExtent // - needed if Map doesn‘t have an extent //---------------------------------- override public function get initialExtent():Extent { return new Extent(-122.539, 45.500, -122.540, 45.501, new SpatialReference(4326)); } //---------------------------------- // spatialReference // - needed if Map doesn‘t have a spatialReference //---------------------------------- override public function get spatialReference():SpatialReference { return new SpatialReference(4326); } //---------------------------------- // tileInfo //---------------------------------- override public function get tileInfo():TileInfo { return _tileInfo; } //---------------------------------- // units // - needed if Map doesn‘t have it set //---------------------------------- override public function get units():String { return "esriDecimalDegrees"; } //-------------------------------------------------------------------------- // // Overridden methods // getTileURL(level:Number, row:Number, col:Number):URLRequest // //-------------------------------------------------------------------------- override protected function getTileURL(level:Number, row:Number, col:Number):URLRequest { // Use virtual cache directory // This assumes the cache‘s virtual directory is exposed, which allows you access // to tile from the Web server via the public cache directory. // Example of a URL for a tile retrieved from the virtual cache directory: // http://serverx.esri.com/arcgiscache/dgaerials/Layers/_alllayers/L01/R0000051f/C000004e4.jpg var url:String = _baseURL + "/L" + padString(String(level), 2, "0") + "/R" + padString(row.toString(16), 8, "0") + "/C" + padString(col.toString(16), 8, "0") + ".png"; return new URLRequest(url); } //-------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------- private function buildTileInfo():void { _tileInfo.height = 512; _tileInfo.width = 512; _tileInfo.origin = new MapPoint(-180, 90); _tileInfo.spatialReference = new SpatialReference(4326); _tileInfo.lods = [ new LOD(0, 0.351562499999999, 147748799.285417), new LOD(1, 0.17578125, 73874399.6427087), new LOD(2, 0.0878906250000001, 36937199.8213544), new LOD(3, 0.0439453125, 18468599.9106772), new LOD(4, 0.02197265625, 9234299.95533859), new LOD(5, 0.010986328125, 4617149.97766929), new LOD(6, 0.0054931640625, 2308574.98883465), new LOD(7, 0.00274658203124999, 1154287.49441732), new LOD(8, 0.001373291015625, 577143.747208662), new LOD(9, 0.0006866455078125, 288571.873604331), new LOD(10, 0.000343322753906249, 144285.936802165), new LOD(11, 0.000171661376953125, 72142.9684010827), new LOD(12, 0.0000858306884765626, 36071.4842005414), new LOD(13, 0.0000429153442382813, 18035.7421002707), new LOD(14, 0.0000214576721191406, 9017.87105013534), new LOD(15, 0.0000107288360595703, 4508.93552506767) ]; } private function padString(text:String, size:int, ch:String):String { while (text.length < size) { text = ch + text; } return text; } } }
customtiled.mxml
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:esri="http://www.esri.com/2008/ags" xmlns:samples="com.esri.ags.samples.*" pageTitle="Extending TiledMapServiceLayer in Flex API"> <!-- @@includeFiles com/esri/ags/samples/PortlandTiledMapServiceLayer.as This sample shows how tiles can be used without the REST endpoint. The ArcGIS API for Flex allows for extending the API to access layer types not included as part of the API. In this example, tiled layers (TiledMapServiceLayer) is extended to access tiles created in 9.2 or above and made accessible in the standard virtual directory. Steps involved: 1. Creating new ActionScript class that extends TiledMapServiceLayer. 2. Hardcoding some of the configuration settings: fullExtent, initialExtent, spatialReference, tileInfo, and units. 3. Overriding the protected function getTileURL() to obtain the appropriate tiles and place them accurately on the map. 4. Using the extended class with MXML or ActionScript :) --> <esri:Map id="myMap"> <samples:PortlandTiledMapServiceLayer id="virtualTiles" fadeInFrameCount="12"/> </esri:Map> <s:Label width="85%" backgroundColor="0xFFFF00" color="0x000000" fontSize="14" fontWeight="bold" horizontalCenter="0" paddingBottom="10" paddingLeft="10" paddingRight="10" paddingTop="10" text="By extending TiledMapServiceLayer, this application can access tiles from an ArcGIS 9.2 (or above) virtual directory. This specific service is in the {virtualTiles.spatialReference.wkid} spatialReference and has {virtualTiles.tileInfo.lods.length} scale levels."/> </s:Application>
以上是关于WebGIS中的坐标系和瓦片地图的主要内容,如果未能解决你的问题,请参考以下文章