简介
公司项目需要用到离线GIS应用,因此做了一个详细的技术调研。时间大概两天,方案选型确定,到服务的搭建,及包含具体应用场景需要的换肤功能。
首先方案大概是有3个。
1.采用拼接地图资源采用image方式渲染整个地图,然后在每个层级的图片上进行点位标注,和数据加载。优点,加载速度快,直接渲染业务需要的地图部分即可,缺点前端工作量大,不利于后续产品化的发展。
2.采用开源的openstreetmap,缺点openstreetmap开源的地图不够详细,没有需要展示的详细资源。
3.采用arcgis方式,下载最新地图的瓦片google,高德等等,自己搭建离线gis服务。缺点,项目应用需要搭建单独的gis服务,优点利于后续产品化的发展,不用单独再造轮子。一次搭建,后续项目可以直接应用。
大体需要的工具及服务, geowebcache+全能电子地图下载器+leaflet
geowebcache为arcgis提供基础的服务,简单说就是把所有的地图资源放到这个服务里面,供你来调用加载,渲染。
全能电子地图下载器,为你提供基础的地图资源(png)图片。
leaflet是地图组件,就是提供地图内的标点,范围等等的一个组件。可以从官网看到,不做过多阐述。
本文只对第三点展开讨论。从0开始的搭建,部署,及展示,以及产品后续需要的换肤功能。服务搭建参考
1.https://www.cnblogs.com/luxiaoxun/p/5022333.html
2.https://www.cnblogs.com/ChineseMoonGod/p/6934928.html
虽然参考博客园的帖子,但是还会单独重新写一遍搭建流程,方便后面小伙伴整体参考吧,会比之前更详细。
服务搭建(geowebcache):
服务下载地址:
https://www.geowebcache.org/
这里要说明,我下载了最新版本,但是地图服务一直没加载出来,具体就是下载了相应的瓦片,但是整个界面是空白。因为时间仓促,这里不得不降级,采用了1.8.0,1.9.0版本均可以正常显示,具体原因未知。后续采用了1.9.0版本。具体调研的版本有:
。
1.运行服务
geowebcache下载之后是一个war包,就是我们传统的web服务。放在tomcat的webapps目录下首先运行起来。
服务运行效果
地址:http://localhost:8080/geowebcache
2.修改配置
打开此文件
配置完之后重启tomcat,会看到
到这里我们暂时停止配置,下载我们需要的地图瓦片。
3.地图资源下载
地图资源下载我采用的是,全能电子地图下载器(破解版)。
工具操作不做过多阐述,应该看看就懂了。
下载好瓦片后,需要进行地图拼接
选择此参数
拼接完成后会生成以下文件
_alllayers内存放就是我们地图的瓦片资源,就是一堆png图片。实际地图渲染的效果就是在服务资源内将png拼在在一起,并且加载web内。
每个层级的地图瓦片资源。
备注:之前参考,说conf.cdi,和conf.xml需要打开,然后保存为unt-8无bom格式,我就直接跟着操作了,具体有bom,和无bom区别我没有具体操作过。操作流程就是,用nodepad++打开两个文件,然后再保存一下。
。
4.继续修改配置
打开我们上一步自动生成的,geowebcache.xml文件。
添加如下几行字符
<arcgisLayer>
<name>ARCGIS-Demo</name>
<tilingScheme>D:\\GisMap\\Layer\\conf.xml</tilingScheme>
<tileCachePath>D:\\GisMap\\Layer\\_alllayers</tileCachePath>
</arcgisLayer>
很明显conf.xml,_alllayers就是我们之前拼接完地图的绝对路径。
接下来重启服务,查看效果。
可以看到我们刚刚配置的服务,点击png即可预览效果
5.对外提供服务
正常应用时,肯定不是预览效果就达到了我们的目的。我们需要调用类似,百度,google地图那种,在我们项目内加载服务,并且根绝第三方的接口进行地图相关的操作,例如标点位信息,做一些面积图处理等等。
服务调用脚本如下:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Leaflet - Offline Demo</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script>
</head>
<body>
<div id="map" style="height:100vh;background-color: #033345" ></div>
<script type="text/javascript">
//地图的中心
var mapCenter = new L.LatLng(28.80748,104.593578);
var map = new L.Map('map', {
center : mapCenter,
zoom:5,//默认展开的地图层级,注意根据业务需求自己调整。
maxZoom:18,//最大层级,注意根据业务需求自己调整。
minZoom:15,//最小层级,注意根据业务需求自己调整。
attribution : '高德普通地图'
});
var wmsLayer = L.tileLayer.wms("http://localhost:8080/geowebcache/service/wms", {
layers: 'ARCGIS-Demo1',//注意这里要和geowebcache.xml内的 <name>ARCGIS-Demo</name>对应
format: 'image/png'
});
wmsLayer.addTo(map);
//点位标记
var marker = new L.Marker([28.80748,104.593578]);
var marker1 = new L.Marker([28.814023,104.571005]);
map.addLayer(marker);
map.addLayer(marker1);
marker.bindPopup("<br>五粮液厂区!</br><button onclick='alert(1);'>进入</button></p>").openPopup();
marker1.bindPopup("<p>红若小学!</br><button onclick='alert(2);'>进入</button></p>").openPopup();
//标记面积
var latlngs2 = [[28.812089,104.589441],[28.810779,104.583208],[28.808918,104.579024],[28.808072,104.579432],[28.805609,104.579432],[28.790829,104.591148]
,[28.802412,104.599473]]; //区域坐标组
var areaLayer = L.polygon(latlngs2, {color: 'red',fillColor:'blue',weight:1}).addTo(map); //添加到地图
</script>
</body>
</html>
这里有两点要说明
1.name属性
layers的name必须配置正确,否则地图不加载。
2.地图中心点
地图点位中心的坐标,就是mapcenter节点的配置,如果不正确,也会导致看不到地图。这里我用的高德在线的取点坐标进行标记。
地址:https://lbs.amap.com/console/show/picker
要注意坐标不要配置反了,例如
5.查看服务运行效果
这样已经达到,调用地图服务资源返回到我们的内部应用中。
6.换肤功能
因为不用应用场景下,可能要求地图的皮肤颜色不同。这里我调研,发现没有直接可应用的地图换肤资源。因此就得动脑子了,因为我们_alllayers文件目录内存储的是默认的所有瓦片资源。那我应该用脚本,把每个对应png的色值重新调整,再保存到目录内,然后调用即可。脚本内容如下:
# *_*coding:utf-8 *_*
from PIL import Image
import os
#
# 遍历文件夹
def walkFile(file):
for root, dirs, files in os.walk(file):
# root 表示当前正在访问的文件夹路径
# dirs 表示该文件夹下的子目录名list
# files 表示该文件夹下的文件list
# 遍历文件
for f in files:
path = os.path.join(root, str(f));
i = 0
j = 0
# path = path.replace("\\","\\")
img = Image.open(path) # 读取系统的内照片
# img = Image.open("D:\\GisMap\\Layer1\\_alllayers\\L05\\R0000000A\\C0000001A.png") # 读取系统的内照片
print (img.size) # 打印图片大小
print (img.getpixel((4, 4)))
width = img.size[0] # 长度
height = img.size[1] # 宽度
for i in range(0, width): # 遍历所有长度的点
for j in range(0, height): # 遍历所有宽度的点
data = (img.getpixel((i, j))) # 打印该图片的所有点
if isinstance(data, tuple) and len(data) == 3:
print (data) # 打印每个像素点的颜色RGBA的值(r,g,b,alpha)
print (data[0]) # 打印RGBA的r值
if (data[0] >= 170 and data[1] >= 170 and data[2] >= 170): # RGBA的r值大于170,并且g值大于170,并且b值大于170
img.putpixel((i, j), (3,52,71,255)) # 则这些像素点的颜色改成大红色
elif (data[0] >= 68 and data[1] >= 68 and data[2] >= 68 and data[0] >= 111 and data[1] >= 111 and data[2] >= 111): # RGBA的r值大于170,并且g值大于170,并且b值大于170
img.putpixel((i, j), (0,94,94,255)) # 则这些像素点的颜色改成大红色
elif (data[0] >= 0 and data[1] >= 0 and data[2] >= 0 and data[0] <= 68 and data[1]<= 68 and data[2]<= 68): # RGBA的r值大于170,并且g值大于170,并且b值大于170
img.putpixel((i, j), (0, 94, 94, 255)) # 则这些像素点的颜色改成大红色
img = img.convert("RGB") # 把图片强制转成RGB
img.save(path) # 保存修改像素点后的图片
def main():
walkFile("D:\\GisMap\\Layer1\\_alllayers")
if __name__ == '__main__':
main()
这里我是随便在高德的皮肤中,取了几个颜色进行替换。但是这里有个遗留问题就是,单线程对每个像素进行替换,效率还是蛮差的。这里还是有很大的优化空间,多线程,对每个层级不同的瓦片资源进行批量更新。
高德官方皮肤截图如下:
脚本处理效果如下:
这里只是粗略的进行几个大的色值替换,同样也有很大的优化空间。
7换肤资源切换
同理,因为我们处理好了另一个皮肤的所有文件后,保存在一个新的_alllayers文件夹内。
再次编辑geowebcache.xml,增加新皮肤的节点信息,然后重启服务。
<arcgisLayer>
<name>ARCGIS-Demo1</name>
<tilingScheme>D:\\GisMap\\Layer1\\conf.xml</tilingScheme>
<tileCachePath>D:\\GisMap\\Layer1\\_alllayers</tileCachePath>
</arcgisLayer>