美文网首页
离线GIS完整解决方案包含换肤2020-02-18

离线GIS完整解决方案包含换肤2020-02-18

作者: 超级小学生1 | 来源:发表于2020-02-19 12:23 被阅读0次

    简介

    公司项目需要用到离线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/

    image.png image.png

    这里要说明,我下载了最新版本,但是地图服务一直没加载出来,具体就是下载了相应的瓦片,但是整个界面是空白。因为时间仓促,这里不得不降级,采用了1.8.0,1.9.0版本均可以正常显示,具体原因未知。后续采用了1.9.0版本。具体调研的版本有:


    image.png

    1.运行服务

       geowebcache下载之后是一个war包,就是我们传统的web服务。放在tomcat的webapps目录下首先运行起来。
    
    image.png
    服务运行效果
    地址:http://localhost:8080/geowebcache image.png

    2.修改配置

    打开此文件


    image.png
    image.png

    配置完之后重启tomcat,会看到


    image.png
    到这里我们暂时停止配置,下载我们需要的地图瓦片。

    3.地图资源下载

    地图资源下载我采用的是,全能电子地图下载器(破解版)。


    image.png

    工具操作不做过多阐述,应该看看就懂了。
    下载好瓦片后,需要进行地图拼接


    image.png

    选择此参数


    image.png

    拼接完成后会生成以下文件


    image.png image.png

    _alllayers内存放就是我们地图的瓦片资源,就是一堆png图片。实际地图渲染的效果就是在服务资源内将png拼在在一起,并且加载web内。

    image.png

    每个层级的地图瓦片资源。

    备注:之前参考,说conf.cdi,和conf.xml需要打开,然后保存为unt-8无bom格式,我就直接跟着操作了,具体有bom,和无bom区别我没有具体操作过。操作流程就是,用nodepad++打开两个文件,然后再保存一下。

    image.png

    4.继续修改配置

    打开我们上一步自动生成的,geowebcache.xml文件。
    添加如下几行字符


    image.png
    <arcgisLayer>
        <name>ARCGIS-Demo</name>
        <tilingScheme>D:\\GisMap\\Layer\\conf.xml</tilingScheme>
        <tileCachePath>D:\\GisMap\\Layer\\_alllayers</tileCachePath>
    </arcgisLayer>
    

    很明显conf.xml,_alllayers就是我们之前拼接完地图的绝对路径。
    接下来重启服务,查看效果。


    image.png

    可以看到我们刚刚配置的服务,点击png即可预览效果


    image.png
    image.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必须配置正确,否则地图不加载。


    image.png

    2.地图中心点

    地图点位中心的坐标,就是mapcenter节点的配置,如果不正确,也会导致看不到地图。这里我用的高德在线的取点坐标进行标记。
    地址:https://lbs.amap.com/console/show/picker
    要注意坐标不要配置反了,例如

    image.png
    image.png

    5.查看服务运行效果

    这样已经达到,调用地图服务资源返回到我们的内部应用中。


    image.png

    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()
    

    这里我是随便在高德的皮肤中,取了几个颜色进行替换。但是这里有个遗留问题就是,单线程对每个像素进行替换,效率还是蛮差的。这里还是有很大的优化空间,多线程,对每个层级不同的瓦片资源进行批量更新。

    高德官方皮肤截图如下:


    image.png

    脚本处理效果如下:


    image.png

    这里只是粗略的进行几个大的色值替换,同样也有很大的优化空间。

    7换肤资源切换

    同理,因为我们处理好了另一个皮肤的所有文件后,保存在一个新的_alllayers文件夹内。


    image.png

    再次编辑geowebcache.xml,增加新皮肤的节点信息,然后重启服务。

    <arcgisLayer>
        <name>ARCGIS-Demo1</name>
        <tilingScheme>D:\\GisMap\\Layer1\\conf.xml</tilingScheme>
        <tileCachePath>D:\\GisMap\\Layer1\\_alllayers</tileCachePath>
    </arcgisLayer>
    
    image.png

    总结:其实很简单的思路,希望可以帮助到后续遇到过类似需求的小伙伴。我的QQ337241905,有相关问题可以联系我。工具具体可以百度搜一下,或者找我要。

    相关文章

      网友评论

          本文标题:离线GIS完整解决方案包含换肤2020-02-18

          本文链接:https://www.haomeiwen.com/subject/yjpnfhtx.html