2DWebGIS简介

2DWebGIS简介

七月 12, 2023

2DWebGIS引擎

GIS简介

地理信息系统(英语:Geographic Information System,缩写:GIS)是一门综合性学科,结合地理学与地图学,已经广泛的应用在不同的领域,是用于输入、存储、查询、分析和显示地理数据的计算机系统,可以分为以下五部分:

  • 人员,是GIS中最重要的组成部分。开发人员必须定义GIS中被执行的各种任务,开发处理程序。熟练的操作人员通常可以克服GIS软件功能的不足,但是相反的情况就不成立。最好的软件也无法弥补操作人员对GIS的一无所知所带来的副作用。
  • 数据,精确可用的数据可以影响到查询和分析的结果。
  • 硬件,硬件的性能影响到处理速度,使用是否方便及可能的输出方式。
  • 软件,不仅包含GIS软件,还包括各种数据库,绘图、统计、影像处理及其它程序。
  • 过程,GIS要求明确定义,一致的方法来生成正确的可验证的结果。

GIS属于信息系统的一类,不同在于它能运作和处理地理参照数据。地理参照数据描述地球表面(包括大气层和较浅的地表下空间)空间要素的位置和属性,在GIS中的两种地理数据成分:空间数据,与空间要素几何特性有关;属性数据,提供空间要素的信息。

软件功能分布

GIS数据的存储、处理、发布和展示是一个完整的WebGIS应用的基本流程。不同的软件或库可能有不同的具体实现方法,但是大致的步骤和原理是相似的。下面我给出一个可能的例子,仅供参考:

  • GIS数据存储:假设我们有一些矢量数据(如shp文件)和栅格数据(如tif文件),我们可以使用PostGIS数据库来存储这些数据。PostGIS是一种开源的空间数据库扩展,可以在PostgreSQL数据库中存储、管理和查询地理信息数据。我们可以使用pgAdmin或者psql等工具来连接PostGIS数据库,创建表,导入数据,设置空间索引等操作。
  • GIS数据处理:假设我们需要对存储在PostGIS数据库中的数据进行一些处理,如投影转换、空间分析、几何运算等。我们可以使用GDAL/OGR库来实现这些功能。GDAL/OGR是一种开源的GIS库,主要用于栅格和矢量数据的格式转换和空间分析。我们可以使用GDAL/OGR提供的命令行工具或者编程接口(如Python)来对PostGIS数据库中的数据进行读写、修改、转换等操作。
  • MWS发布服务:假设我们需要将处理好的数据发布为Web服务,以便在Web端进行访问和展示。我们可以使用GeoServer来实现这个功能。GeoServer是一种开源的GIS服务器软件,可以发布OGC标准的Web服务,如WMS、WFS、WCS、WMTS等。我们可以使用GeoServer提供的Web管理界面来配置数据源(如PostGIS)、创建图层、设置样式、发布服务等操作。
  • Web端展示数据:假设我们需要在Web端展示发布好的Web服务,以便用户进行交互和查询。我们可以使用OpenLayers或者Leaflet等开源的Web GIS框架来实现这个功能。OpenLayers和Leaflet都是基于JavaScript的客户端库,可以加载多种类型和来源的地图图层,提供丰富的地图控件和交互功能。我们可以使用HTML、CSS和JavaScript来编写Web页面,调用OpenLayers或者Leaflet提供的API来加载GeoServer发布的Web服务,并设置地图视图、控件、事件等 。

GIS坐标系定义

2DWebGIS解决方案

LeafLet

渲染地图方法

Leaflet可以使用Canvas或者非Canvas的DOM元素来渲染地图,具体取决于图层的类型和设置。一般来说,矢量图层(如L.Polyline、L.Polygon、L.CircleMarker等)可以使用Canvas或者SVG来渲染,而栅格图层(如L.TileLayer、L.ImageOverlay等)只能使用非Canvas的DOM元素(如)来渲染。你可以通过设置L_PREFER_CANVAS为true来让Leaflet尽可能地使用Canvas来渲染矢量图层,这样可以提高性能和质量,但是会失去事件监听和交互功能。你也可以通过设置L_NO_TOUCH为true来让Leaflet尽可能地使用非Canvas的DOM元素来渲染矢量图层,这样可以提供事件监听和交互功能,但是会降低性能和质量。

根据地图状态得到可视区域信息

  • 地图使用L.CRS.EPSG3857作为crs属性,这是一个常用的地理坐标系统,它将经纬度坐标投影为墨卡托坐标。
  • 地图使用[51.505, -0.09]作为center属性,这是一个经纬度坐标,表示伦敦的位置。
  • 地图使用13作为zoom属性,这是一个整数,表示地图的缩放级别。
  • 地图使用[800, 600]作为窗口的大小,这是一个像素坐标,表示地图在页面上的宽度和高度。
  • 我们想要获取地图当前显示的Bounds,也就是经纬度范围。

那么,我们需要进行以下几个步骤:

  • 首先,我们需要将center属性转换为CRS坐标,也就是墨卡托坐标。我们可以使用地图的crs属性提供的project()方法来进行这一步。具体来说,我们需要调用L.CRS.EPSG3857.project(L.latLng(51.505, -0.09))来得到结果。结果是一个Point对象,表示[-10065.738, 6711373.753]
  • 然后,我们需要将CRS坐标转换为像素坐标。我们可以使用地图的zoom属性来进行这一步。具体来说,我们需要将CRS坐标乘以2的zoom次方来得到结果。结果是一个Point对象,表示[-1610528.0, 107382796.1]
  • 接着,我们需要将像素坐标转换为窗口坐标。我们可以使用地图的窗口的大小来进行这一步。具体来说,我们需要将像素坐标加上窗口宽度和高度的一半来得到结果。结果是一个Point对象,表示[-801028.0, 53782796.1]
  • 最后,我们需要根据窗口坐标来获取Bounds。我们可以使用地图提供的unproject()方法来进行这一步。具体来说,我们需要分别对窗口左上角和右下角的坐标进行反投影,并构造一个LatLngBounds对象来得到结果。结果是一个LatLngBounds对象,表示[[51.488, -0.112], [51.522, -0.068]]

请求数据

WMS

如果使用wms,我们需要根据crs、LatLngBounds和zoom来构造一个合适的wms请求,并发送给wms服务地址。具体来说,我们需要将LatLngBounds转换为CRS坐标,并作为bbox参数;我们需要将crs作为srs参数;我们需要根据窗口的大小来设置width和height参数;我们需要指定request为GetMap,并选择合适的layers、styles和format参数。然后,我们会得到一个完整的wms请求URL,例如:http://ows.mundialis.de/services/service?request=GetMap&layers=OSM-WMS&styles=&format=image/png&width=800&height=600&srs=EPSG:3857&bbox=-10065.738,-0.09,6711373.753,51.505。我们可以将这个URL作为一个ImageOverlay对象添加到地图上,或者将其作为一个TileLayer.WMS对象添加到地图上(这样会自动切分为多个小图片)。

TMS

如果使用tms,我们需要根据crs、center和zoom来构造一个合适的tms请求,并发送给tms服务地址。具体来说,我们需要将center转换为CRS坐标,并根据zoom计算出其对应的瓦片行列号;我们需要指定layers为想要显示的图层名称;我们需要将zoom、瓦片行号和瓦片列号作为URL路径的一部分。然后,我们会得到一个完整的tms请求URL,例如:http://tile.openstreetmap.org/13/4092/2729.png。我们可以将这个URL作为一个TileLayer对象添加到地图上。

  • 首先,我们需要将center属性转换为CRS坐标,也就是墨卡托坐标。我们可以使用地图的crs属性提供的project()方法来进行这一步。具体来说,我们需要调用L.CRS.EPSG3857.project(L.latLng(51.505, -0.09))来得到结果。结果是一个Point对象,表示[-10065.738, 6711373.753]。
  • 然后,我们需要将CRS坐标转换为像素坐标。我们可以使用地图的zoom属性来进行这一步。具体来说,我们需要将CRS坐标乘以2的zoom次方来得到结果。结果是一个Point对象,表示[-1610528.0, 107382796.1]。
  • 接着,我们需要将像素坐标转换为瓦片坐标。我们可以使用地图提供的getTileSize()方法来进行这一步。具体来说,我们需要将像素坐标除以瓦片大小(默认为256)来得到结果。结果是一个Point对象,表示[-6292.5, 419320.6]。
  • 最后,我们需要将瓦片坐标转换为TMS请求参数。我们可以使用地图提供的wrapLatLng()方法和tms属性来进行这一步。具体来说,我们需要将瓦片坐标取整并限制在有效范围内(0到2的zoom次方减1)来得到瓦片行列号;如果tms属性为true(默认为false),我们需要将瓦片行号取反(即2的zoom次方减1减去瓦片行号)来适应TMS的Y轴方向;然后,我们需要将图层名称、缩放级别、瓦片行号和瓦片列号作为URL路径的一部分。结果是一个完整的TMS请求URL,例如:http://tile.openstreetmap.org/13/4092/2729.png。

渲染

WMS

如果使用wms得到底图数据,我们可以将其作为一个ImageOverlay对象添加到地图上。ImageOverlay对象会创建一个元素,并将其添加到地图的.leaflet-overlay-pane容器中。ImageOverlay对象会根据地图的crs、center、zoom属性计算出图片的经纬度范围,并使用CSS样式中的transform属性来设置图片的位置和大小。例如,如果图片的经纬度范围为[[51.488, -0.112], [51.522, -0.068]],那么ImageOverlay对象会将其转换为CRS坐标(墨卡托坐标),然后根据缩放级别转换为像素坐标,最后根据中心位置转换为窗口坐标。假设窗口坐标为[376, -184],那么ImageOverlay对象会使用CSS样式中的transform: translate3d(376px, -184px, 0px)来设置图片的位置。

TMS

如果使用tms得到底图数据,我们可以将其作为一个TileLayer对象添加到地图上。TileLayer对象会创建多个元素,并将其添加到地图的.leaflet-tile-pane容器中。TileLayer对象会根据地图的crs、center、zoom属性计算出需要加载哪些瓦片,并使用CSS样式中的transform属性来设置瓦片的位置和大小。例如,如果瓦片的行列号为[4092, 2729],那么TileLayer对象会根据缩放级别计算出瓦片对应的像素坐标,最后根据中心位置转换为窗口坐标。假设窗口坐标为[376, -184],那么TileLayer对象会使用CSS样式中的transform: translate3d(376px, -184px, 0px)来设置瓦片的位置。

OpenLayers

架构设计

流程简述

  1. 假设我们有一个地图(map),它包含了一个瓦片图层(tileLayer),这个图层使用了一个TileWMS源(tileWMSSource)来获取WMS服务的瓦片。
  2. 当用户使用拖拽交互(dragPanInteraction)来平移地图时,这个交互会改变view的中心点(center)属性,并触发view的change:center事件。
  3. 当view的change:center事件被触发时,map会监听到这个事件,并调用map.render()方法来重新渲染地图。
  4. 当map.render()方法被调用时,map会遍历它的所有图层,并调用每个图层的render()方法来重新渲染图层。
  5. 当tileLayer的render()方法被调用时,tileLayer会根据view的分辨率(resolution)和范围(extent)来计算需要显示的瓦片坐标(tileCoord),并调用tileWMSSource的getTile()方法来获取对应的瓦片。
  6. 当tileWMSSource的getTile()方法被调用时,tileWMSSource会根据瓦片坐标(tileCoord)、投影(projection)和其他参数(params)来生成一个WMS请求的url,并发送一个HTTP请求到WMS服务端。
  7. 当WMS服务端收到HTTP请求时,它会根据请求的url中的参数,如LAYERS、BBOX、WIDTH、HEIGHT等,来动态生成和返回一个地图瓦片图片。
  8. 当tileWMSSource收到WMS服务端返回的地图瓦片图片时,它会将这个图片缓存起来,并触发tileload事件,通知tileLayer瓦片已经加载完成。
  9. 当tileLayer监听到tileload事件时,它会将这个图片添加到DOM中,并显示在地图上。

重投影(Reprojection)

OpenLayers是通过以下步骤来针对每个栅格进行坐标转换并重采样的:

  1. 首先,OpenLayers会根据目标坐标系(view的projection)和原始坐标系(source的projection)来创建一个ol/reproj/Tile对象,这个对象表示一个重投影后的瓦片。
  2. 然后,OpenLayers会根据重投影后的瓦片的范围(extent)和分辨率(resolution)来计算需要请求的原始瓦片的范围(extent)和分辨率(resolution),并调用source的getTile()方法来获取对应的原始瓦片。
  3. 接着,OpenLayers会根据原始瓦片的范围(extent)和分辨率(resolution)来创建一个ol/reproj/Triangulation对象,这个对象表示一个三角剖分,用来描述原始坐标系和目标坐标系之间的变换关系。
  4. 最后,OpenLayers会根据重投影后的瓦片的大小(width和height)、原始瓦片的图片(image)、三角剖分对象(triangulation)以及插值方法(interpolation)来创建一个Canvas对象,并使用Canvas.getContext(‘2d’).drawImage()方法来绘制重采样后的图片。

参考资料

  1. https://zh.wikipedia.org/zh-cn/%E5%9C%B0%E7%90%86%E4%BF%A1%E6%81%AF%E7%B3%BB%E7%BB%9F
  2. https://www.esri.com/en-us/arcgis/products/index
  3. https://blog.csdn.net/weixin_43715910/article/details/108350962
  4. https://blog.csdn.net/guo45682/article/details/117792157
  5. https://zhuanlan.zhihu.com/p/102923131
  6. https://postgis.net/
  7. https://gdal.org/
  8. https://docs.geoserver.org/stable/en/user/
  9. https://openlayers.org/
  10. https://leafletjs.com/
  11. https://www.template.net/graphic-design/leaflets/
  12. https://design.tutsplus.com/articles/what-is-a-leaflet—cms-37850
  13. https://dribbble.com/tags/architecture%20brochure
  14. https://www.creativevivid.com/design/brochures/architecture-brochure/
  15. https://stackoverflow.com/questions/12735303/how-to-change-the-map-center-in-leaflet-js
  16. https://stackoverflow.com/questions/20527296/leaflet-js-center-and-zoom-on-feature-group
  17. https://stackoverflow.com/questions/38351570/match-center-of-a-tile-layer-with-center-of-the-map-on-leaflet
  18. https://gis.stackexchange.com/questions/423633/calculating-leaflet-scale-ratio-for-the-particular-zoom-level
  19. https://stackoverflow.com/questions/34137648/leaflet-bounds-with-padding
  20. https://vue2-leaflet.netlify.app/examples/set-bounds.html
  21. https://stackoverflow.com/questions/65966825/how-to-bounds-in-leaflet
  22. https://stackoverflow.com/questions/46544933/what-is-main-difference-between-web-map-service-and-tile-map-service
  23. https://stackoverflow.com/questions/41631573/how-to-calculate-tiles-coordinates-in-leaflet-to-request-tms-server
  24. https://stackoverflow.com/questions/37674478/leaflet-data-visualization-framework-export-as-image
  25. https://gis.stackexchange.com/questions/269711/rotating-current-map-extent-around-center-point-in-openlayers
  26. https://www.wolfram.com/language/12/new-in-geography/country-polygon-distortion-in-the-mercator-projection.html.zh
  27. https://en.wikipedia.org/wiki/2.5D
  28. https://iclient.supermap.io/web/books/modern-web-gis-in-action/12xian-dai-webgis-de-ti-xi-jie-gou.html