MapServer+OpenLayers5+Vue实现栅格图层数据查询

0、先上效果图

MapServer+OpenLayers5+Vue实现栅格图层数据查询_第1张图片

1、Mapserver基础与配置

【注意:以下内容默认已经安装了MapServer4.0及以上版本,Web GIS使用Openlayers5作为请求和地图渲染库,ol-ext作为弹窗UI】

1.1、MapServer基础

MapServer是一个流行的开源项目,其目的是能够通过网络服务发布用户自定义的地图[1]

  1. 支持显示和查询多种栅格、矢量数据格式;
  2. 能够在各种操作系统上运行(Windows, Linux, Mac OS X等);
  3. 支持流行的脚本语言和开发环境(PHP、Python、Perl、Ruby、Java、.NET);
  4. 动态投影;
  5. 高质量渲染;
  6. 等;

在其最基本的形式中,MapServer是一个在Web服务器上非活动的CGI程序。当一个请求被发送到MapServer时,它使用在请求URL和Mapfile中传递的信息来创建被请求映射的图像。请求也可能返回图例、比例条、参考地图和以CGI变量传递的值的图像。(前端请求常用的方式)

当然,MapServer也可以通过MapScript脚本语言或templating进行扩展和定制,可以构建它来支持许多不同的矢量和栅格输入数据格式,并且可以生成多种输出格式。大多数预编译的MapServer发行版都包含了大部分MapServer的特性。有兴趣请到官网查看

1.2、MapServer前端请求准备

       Mapfile:Mapfile文件包含了对应地理数据在MapServer的配置,包括渲染配置和查询配置等,用于告诉MapServer能对里面涉及的地理数据进行什么样的操作[1]。一份Mapfile中能够定义多个地理数据图层,类似于ArcGIS的*.mxd文件。

       地理数据:MapServer会通过Mapfile中定义的图层(Layer)的路径读取实际的地理数据,因此,在Mapfile中定义的图层都是需要有实际数据存在,否则会导致MapServer读取数据失败。

       HTML页面:MapServer内部提供了默认的HTML页面,当进行查询时,MapServer会将查询到的结果放在HTML页面中返回。当然,用户也可以自己定义符合自己要求的HTML页面。对于查询数据属性而言,HTML页面是必须存在,否则无法触发MapServer的查询机制。

       MapServer CGI:有了前面三类材料,前端进行查询还需要一个通用接口,这个接口称为公共网关接口(Common Gateway Interface,CGI),是Web 服务器运行时外部程序的规范,按CGI 编写的程序可以扩展服务器功能。CGI 应用程序(MapServer CGI程序名为mapserv)能与浏览器进行交互,还可通过数据API与数据库服务器等外部数据源进行通信,从数据库服务器中获取数据[2]。格式化为HTML文档后,发送给浏览器,也可以将从浏览器获得的数据放到数据库中。MapServer CGI一般通过表单提交,参数较多,目前常用方法是通过OpenLayers封装的方法获取对应的URL[10]

       Web服务器:要使得MapServer查询能够通过网络访问,计算机中还需要有一个公有IP地址和配置Web服务访问。

1.3、单个栅格图层Mapfile构建

       其基本结构为[3,4,5] (每次缩进都是2个英文的空格,每个标签后值的填写都需要空一格。注意:以下用#标注注释每个标签的含义,在实际文件中不用出现。加粗的字体都是为实现栅格图层查询所必须加的配置,矢量查询也类似)

MAP #Mapfile的根标注
  NAME #为该文件命名,可以与其他文件重复,用于标注属于哪个服务
  STATUS #文件激活状态,默认为ON,表示运行读取该文件
  SIZE #每次返回地图的大小(一般请求地图是使用WMSTile方式)
  EXTENT #配置该文件中图层的显示范围
  UNITS #图层的显示单位
  SHAPEPATH #重要,用于指示地理数据存放的绝对路径
  IMAGECOLOR #返回地图的背景颜色
  FONTSET #能够使用的字体,如标注等
  WEB #配置Web服务的元数据,以下给出常用的的6个字段
    METADATA
      "wms_title" "WMS Demo Server"	
      "wms_onlineresource" "http://localhost:8091/cgi-bin/mapserv.exe?"	
      "wms_srs" "EPSG:4326 EPSG:3857"	
      "wms_enable_request" "*"	
      "queryable" "true"	 #查询栅格数据必须要有的一项,指出可查询
      "wms_feature_info_mime_type" "text/html"	#查询栅格数据必须要有的一项,指出查询结果使用html方式返回,同时需要在LAYER中指定模板html
    END
  END
  PROJECTION #返回地图的投影
    "init=epsg:3857" #在Openlayers显示中,默认投影是3857
  END
  LEGEND #图例配置
    KEYSIZE #大小
    LABEL	 #图例内部字体的标签配置
      TYPE BITMAP #暂时不懂,默认使用BITMAP
      SIZE #字体排版方式,一般为MEDIUM
      COLOR #字体的颜色
    END
    STATUS #图例的激活状态,一般默认填写ON
  END
  LAYER
	NAME #图层的名字
	DATA #图层的文件名,结合SHAPEPATH得到文件的绝对路径
	PROJECTION #可以定义具体图层的投影
	  "init=epsg:4326"
    END
	TYPE #指出数据是什么类型的,栅格为RASTER,点数据为POINT
	STATUS #图层的激活状态,一般为DEFAULT
	SIZEUNITS #单元大小,栅格图层一般设置为PIXELS
	TOLERANCEUNITS #栅格容限单位大小,一般设置为PIXELS
	DUMP #是否允许保存查询,一般设置为ON
	TEMPLATE #指出查询结果返回的模板,必须有,否则无法查询数据
	METADATA #图层的元数据,查询数据时必须添加
	  "gml_include_items" "all"	
	   "wms_include_items" "all"
	END
	CLASS
	  NAME #类别1的名称
	  EXPRESSION #表达式,用于判断哪个值区间内属于该类别,图层值使用[pixel]进行获取,可以作简单的运行
	  STYLE #显示的样式
		COLOR #显示的颜色
	  END
	  LABEL #可选项,可以配置在像素上显示标注,一般用于矢量数据
	  END
	END
	CLASS
	  NAME #类别2的名称
	  EXPRESSION #表达式,用于判断哪个值区间内属于该类别,图层值使用[pixel]进行获取,可以作简单的运行
	  STYLE #显示的样式
		COLOR #显示的颜色
	  END
	END
  END
END

 

1.4、Template HTML页面构建

1.4.1、栅格查询返回的结果[6]

       x: 查询栅格像素的经度,该坐标的地理参考系与LAYER中的设置的相同

       y: 查询栅格像素的纬度,该坐标的地理参考系与LAYER中的设置的相同

       value_list:如果栅格为多波段数据,则返回每个图层在相同位置的查询结果

       value_n: 在所选列表中该像素处的第n个频带的值(基于0)。每个选择的波段都有一个value_n条目。

       class: 此像素所属的类的名称(仅为分类层)。

       red、green、blue:该像素的颜色

1.4.2、用HTML页面封装的结构[7]

       整体上为通用的HTML结构,但第一行必须添加MapServer的模板识别字符串。

 #该行是为了能让MapServer识别这是模板



  

  

通过可以将查询的信息构建为一份表格,返回HTML后可以直接在前端显示。获取查询的数据使用中括号[字段],如果该HTML不直接作为显示,而是仅作为封装,则建议在td标签添加name字段,以便用于前端HTML数据的解析。例子:




    MapServer Template Sample


	
name latitude longitude value
[Layer] [x] [y] [value_0]

2、基于Openlayers5的栅格数据查询

//请求与显示思路:

//1、绑定map的单击事件;

//2、监听map的单击事件,并获取单击的像素投影坐标;

//3、将投影坐标转为地理坐标;

//4、获取需要查询的栅格图层,并基于该图层构建CGI查询的URL;

//5、发送请求,得到数据并进行解析;

//6、重复步骤4,直至所有栅格图层查询完毕;

//7、在地图上显示查询信息【第3节】

//8、解绑map的单击事件【第3节,当不需要再查询的时候触发】

//代码例子:

import {toLonLat} from 'ol/proj';


data() {
  return {
    clickEvent: null,
  }
}


queryLayerInfo(){
  //捆绑监听事件
  this.clickEvent = this.basemap.on('singleclick', this.mapSingleClick); 
}

mapSingleClick(evt) {
  let layers = this.basemap.getLayers().getArray();
  //将投影坐标转为地理坐标
  let coordinate = toLonLat(evt.coordinate);
  let infoModel = {'longitude': coordinate[0].toFixed(5), 'latitude': coordinate[1].toFixed(5)};//用于存储多个栅格图层的像素值对象
let viewResolution = 0.001;
  for (let i = 0; i < layers.length; i++) {
    //挑选出可以查询的图层
    if (layers[i].get('name') !== undefined && layers[i].get('name') !== 'baseVectorLyr') {
      let url = layers[i].getSource().getGetFeatureInfoUrl(
        coordinate,
        viewResolution,
        'EPSG:4326',
        {
          'INFO_FORMAT': 'text/html',
          'QUERY_LAYERS': layers[i].get('name')
        }
      );
      $.ajax({
        type: 'GET',
        url: url,
        async: false,
        success: function (res) {
          let el = document.createElement('html');
          el.innerHTML = res;
          let tr = el.getElementsByTagName('td');
          if (tr.length > 0){
            infoModel[tr[0].innerText] = tr[1].innerText;
          }
        }
      });
    }
  }
},

注意事项【重要】

  1. toLonLat(evt.coordinate),默认的地理坐标与map.getView.getProjection的相同;
  2. 通过WMSTile加载MapServer的地图都会有一个图层名称,对于底图则没有,由于底图不具备查询功能,因此在查询的时候需要将其过滤掉。
  3. layers[i].getSource().getGetFeatureInfoUrl()方法会自动补充MapServer CGI的路径,同时补充一些通用参数,如Service、Version。而‘epsg:4326’要对应LAYER中设置的投影坐标,一般同一个系统内建议用相同的坐标[8]
  4. coordinate为像素的地理坐标,getGetFeatureInfoUrl()方法会将该像素的地理坐标转换为像素在图层笛卡尔坐标系的坐标(即像素在图层中的行列号),在URL中参数为I/J;
  5. viewResolution为查询的分辨率,这个参数十分重要,决定了查询的精度,如果分辨率太大,则无论点击那个地方,都会得到相同的值。经过测试,viewResolution为0.001及更小时,能保证得到I/J值是正确的[8]
  6. INFO_FORMAT为返回结果的格式,默认的是text/plain格式,该格式返回的数据结构不利于文本解析,建议设置为text/html,这样返回的结构就是template HTML中的结构;
  7. QUERY_LAYERS为查询图层的名字,CGI URL中的必需参数,缺少了则会报错;
  8. 请求方式目前在使用vue-axios时报了跨域错误,因此,在本方法中使用ajax进行请求;
  9. 后台返回的数据格式固定是html,因此,需要使用js对html进行解析:首先通过document创建一个元素,再将返回的html全部作为该元素的内部内容,之后就可以用过getElementsByTagName(tag_name)方法提取出图层名字和像素值了。【由于返回的经纬度在查询之间就知道了,所以在这里就缺省了经纬度数据的返回,仅返回了图层名字和像素值】

3、Openlayers-Extend显示

//显示思路:

//7、在地图上显示查询信息【第3节】

//8、解绑map的单击事件【第3节,当不需要再查询的时候触发

import Popup from 'ol-ext/overlay/Popup'
import {unByKey} from 'ol/Observable'

//向map中添加popup图层
let popup = new Popup({
  popupClass: 'default anim',
  closeBox: true,
});
map.addOverlay(popup);


//根据结构化的查询数据构建HTML内容
getShowContent(infoModel){
  if (!infoModel) return '';
  let html = ol_ext_element.create('DIV', { className: 'ol-popupfeature' });
  // 设置标题
  let title = 'Raster Layer Information: ';
  ol_ext_element.create('H3', { html:title, parent: html });
  // 构建表格
  if (infoModel) {
    let tr, table = ol_ext_element.create('TABLE', { parent: html });
    for (let att in infoModel) {
      let value = infoModel[att];
      tr = ol_ext_element.create('TR', { parent: table });
      //添加字段
      ol_ext_element.create('TD', {
        html: att.substring(att.lastIndexOf('/')+1), parent: tr
      });
      // 添加值
      ol_ext_element.create('TD', {
        html: value,
        parent: tr
      });
    }
  }
  return html;
}
//获取html内容
let htm = this.getShowContent(infoModel);
//在指定投影坐标上弹出内容

this.popup.show(coordinate, htm);

//如果不需要再点击查询了,可以解除监听,避免每次点击都被认为是在查询
unByKey(this.clickEvent);//取消监听事件

注意事项【重要】

以上代码为多个文件中截取出,不宜直接复制粘贴。理解思路才是最重要的,代码书写形式次要。Openlayers-Extend的关于Popup的API如下[9]

参考资料

[1]https://mapserver.org/introduction.html#mapserver-overview

[2]https://baike.baidu.com/item/CGI/607810?fr=aladdin

[3]https://mapserver.org/mapfile/index.html#mapfile

[4]https://mapserver.gis.umn.edu/de/tutorial/section1.html

[5]http://augusttown.blogspot.com/2010/01/customize-wms-getfeatureinfo-response.html

[6]https://mapserver.org/input/raster.html

[7]https://mapserver.org/mapfile/template.html#template

[8]https://stackoverflow.com/questions/46095654/strange-error-returned-while-trying-to-access-raster-pixel-value

[9]http://viglino.github.io/ol-ext/doc/doc-pages/ol.Overlay.Popup.html#show

[10]https://mapserver.org/cgi/controls.html

 

 

你可能感兴趣的:(Web,GIS,GIS,Server,js,vue,服务器)