• 开源GIS解决方案之路


    好久没更新了,因为我在--憋--大--招--,对,就是今天这篇。

    今天跟大家分享一下我的开源GIS解决方案经历。

    --额-- 考虑到单聊技术解决方案你可能会很快睡着,所以我今天会把重点放在我封装地图API这个事情上,以封装地图API的经历为线索,穿插着讲一些当时用到的开源GIS架构。

    文章稍微有点长,如果你只是想了解一下最新的开源GIS架构,可以直接跳过前面,去看第五版和最后的总结,但我建议你还是从第一版开始看,因为没有前面的 4 个版本就不会有第五版,只看总结就和读名言警句效果一样,看的时候觉得有道理,过后就忘了,因为不能感同身受。

    缘起

    我一直在传统IT行业的公司工作,公司都是以做政府项目为主,俗称toG业务。

    这种公司里,GIS在其中的应用通常为两种,一种是GIS结合公司的业务形成一个xx地理信息系统,或是xx一张图系统,另一种是结合公司业务封装一套地图API,为公司的其它业务系统提供地图技术支撑,类似于高德地图API、百度地图API。地图API的背后通常有一套GIS解决方案作为支撑

    今天我们就来聊一聊封装地图API这点事。

    从我封装的第一个地图API版本诞生到现在,已经过去了7年时间,中间经历了 3 家公司,迭代了 5 个版本。5 个版本中,第 1 个版本使用的ArcGIS,后面 4 个版本主要使用的开源GIS。

    第一版 2014年

    背景

    公司做环保业务,业务系统主要用C#开发,业务系统中涉及地图的部分使用ArcGIS Flex API开发,由GIS开发人员负责。那个年代,地图还主要是用Flex开发,因为Flex在那时给人感觉是很炫酷。

    随着项目的增加,发现很多业务功能在项目里是通用的,再后来发现,如果把业务功能和地图功能分离,再把Flex编写的地图功能封装一套面向JS的接口,C#开发人员就可以自己完成地图相关的工作,不用受Flex语言的限制了。

    于是我们准备封装一套用Felx编写的,面向C#开发人员的JS地图API。

    技术架构

    1. 前台使用ArcGIS Flex API开发地图功能,Flex支持和JS交互,利用这一特性将Flex开发的地图功能,封装成面向JS的接口。
    2. 地图后台使用 ArcGIS Server,空间分析使用 GP 服务。
    3. 空间数据库使用 Arc SDE。
    4. 开发了一个简单的帮助网站,提供前台JS接口的调用示例和使用说明。

    经验总结

    地图API发布后,在做技术支持的过程中发现一个有趣的现象,关于地图API的使用,完全不懂GIS的初级C#开发人员觉得好用,原因是能帮他们解决问题,有困难时可以随时找我们技术支持。

    了解一点GIS的中级C#开发人员觉得不好用,他们会拿我们的地图API和ArcGIS JS API对比,觉得后者更好用,但由于ArcGIS JS API的地图偏丑,我们也不提供技术支持,需要他们自己去研究,最终还是选择用我们的地图API。

    了解一点GIS的高级C#开发人员基本不用,其中有两个同事的反应令我印象深刻,一个同事说:”你们自己开发的东西,自己都不用“。言外之意是,你们自己都不用的东西不会好用。但我们的想法是,把Flex封装一套JS的地图接口是因为Flex入门有门槛,我们GIS开发人员既然都会Flex了,而且我们开发的地理信息系统,整个都是用Flex开发的,那肯定是直接用ArcGIS Flex API会更灵活,所以不用。

    还有一个同事更牛,他直接去研究Flex,不会的就问我们,入门后直接封装了一套地图接口自己用。我们研究过他封装的接口,虽然功能简单了些,但定义接口时的出发点感觉明显和我们不一样,我们是站在功能的角度封装,尽量保证接口的复用度高,比如添加点,添加线,缓冲等功能。他是站在用户的角度封装,比如从数据库查出来一堆数据,把这堆数据直接丢给接口,就在地图上展示出来了。总体而言,他的接口封装度更高,更贴近他的实际使用习惯,而我们的接口更像是把ArcGIS的Flex接口翻译了一套JS的。

    还注意到一个现象,经常有使用我们接口的业务开发人员跑过来问,为什么我的地图不显示?经历的多了,发现通常有两种原因,一种是地图服务的地址不对,当时的地图服务都是用ArcGIS server发布的,ArcGIS server地图服务的rest地址是个网页,这个网页打开后,有很多级的链接地址,业务人员不知道应该拷贝哪一级的地址,经常拷错,所以地图出不来。另一种是,添加多个地图时,地图间的坐标不一致,导致添加的地图不显示。

    想想也是,地图服务和地图坐标这些知识对于不懂GIS的开发人员来说还是挺难的。

    虽然第一版有这样那样的问题,但在当时还是提升了部门的整体工作效率。不光是C#开发人员可以自己去开发地图功能了,GIS组内部也通过这种形式,把分散在各个项目中的技术成果收集了起来,并不断的积累完善。

    第二版 2016年

    背景

    换公司了,新公司做网安业务,有海量定位数据,GIS在其中的作用是,对定位数据进行提取、分析、展示,从而帮助客户解决业务问题。

    公司的所有系统必须部署在客户内网,客户的内网是无法访问互联网的,而地图使用的又是互联网地图,这就需要把互联网地图瓦片下载下来,拷贝到客户内网发布。

    公司有一个GIS应用系统,和GIS强关联的业务功会能放在这里。另外,其它部门有地图功能需求时,也会找到我们GIS部门,这个场景和上家公司很像。

    所以,二话不说,第二版地图API走起。

    技术架构

    1. 这一版开始使用开源GIS。GIS前台选择了openlayers,有了第一版的经验,这一版重点解决了地图资源问题,和地图坐标问题。
      1. 解决地图资源问题。统一管理底图资源,提供多种互联网地图,包括高德地图、谷歌地图、天地图等,每种地图有个id,初始化地图时,根据id使用不同的底图。
      2. 解决地图坐标问题。对外统一使用wgs84坐标,地图API内部负责将wgs84坐标转换为和底图匹配的坐标,包括gcj02坐标、bd09坐标等。
    2. GIS后台方面,因为定位数据都存放在大数据框架的数据库中,后台只有互联网离线地图瓦片需要发布,所以直接使用了tomcat发布瓦片,再在openlayers中写一个加载本地瓦片的功能,这就算GIS后台了。
    3. 没有使用空间数据库,空间分析使用ArcGIS Engine开发控制台程序,再用Java后台调用控制台程序。
    4. 接口写了详细的说明文档和调用示例。没有做包装,直接是word文档+html示例文件。

    经验总结

    1. 解决地图资源和坐标问题,可以大大提升用户体验。
    2. 关于地图下载器,虽然大家都有能力自己写一个出来,但真的挺不划算的,最好的解决方案就是花公司的钱去买一个许可,站在公司的角度这都没几块钱。
    3. ArcGIS Engine的版本,C#版的比较稳定,Java版的超级难用,非常不稳定,动不动就死给你看,更不要尝试去把它部署到Linux服务器,不要问我是怎么知道的。我当时为了在linux上部署,先开发了一版java的,部署后一天崩溃好几次,动不动就内存溢出,根本没法用,没办法只能重新写了一版C#的部署在windows服务器上。

    第三版 2017年

    背景

    换公司了,新公司做管网业务,相比前面两家,业务和GIS的相关性更强一些,业务中需要对管网GIS数据进行编辑、存储、发布和应用。公司之前地图都是使用ArcGIS开发的,正在准备转开源GIS,于是我到公司后就顺理成章的开始了第三版地图API的开发。

    在开发之前,我先仔细研究了高德和百度地图API,并问了自己两个问题:

    一、为什么非GIS开发人员,能够用高德、百度地图API解决问题,却用不了ArcGIS,openlayers,leaflet?

    二、非GIS开发人员在用互联网地图API时遇到了哪些问题?

    下面是我自己的理解:

    第一个问题是因为:1、非GIS开发人员不需要自己发布地图数据,地图都是官方提供的,只管用就行。2、互联网地图API的帮助文档和示例都是中文的。

    第二个问题,我自己尝试使用后发现:1、互联网地图API的离线使用是个问题,它们都只能在线使用,如果遇到不能访问互联网的情况,就无法使用了,这问题在toG业务中还是挺常见的。2、互联网地图API只能用官方提供的地图坐标,不能集成其它坐标的地图资源。

    从这一版起,我们开始尝试在用户体验上对标高德、百度地图API,学习对方的优点,避免对方的问题。

    技术选型

    GIS前台没有再继续使用openlayers,而是转向了更为轻巧的leaflet,当时的考虑是:

    1. leaflet很小巧,核心代码结构简单容易理解,可塑性强,适合拿来改造为自己的API。
    2. leaflet可以同时兼容web端和移动端。
    3. 有esri维护的leaflet插件,能更好的兼容公司之前发布的ArcGIS地图服务。

    GIS后台选择了geoserver。因为geoserver的资料比较丰富,能同时支持postGIS和SDE空间数据库。

    GIS空间数据库选择了 postGIS,空间分析也主要使用 postGIS 的空间分析函数实现。

    桌面端开发继续使用ArcGIS Engine,没有去尝试QGIS,主要原因是,公司之前在ArcGIS Engine上有大量的技术积累,已经形成了成熟的产品,转换成本会很高。

    技术架构

    在leaflet的基础上封装了一层自己的接口,原生leaflet接口不对外,当时的考虑是:

    1. 和上一版一样,封装后容易解决互联网地图偏移的问题,对外统一使用wgs84坐标,内部根据不同的底图将数据转换为对应的坐标。
    2. 可以实现类似JQuery那样的扁平化接口,简单易用。
    3. leaflet中点的写法是[纬度,经度],而我们通常更习惯使用[经度,纬度]的写法,可以通过封装顺便解决这个问题。

    当需要复杂的GIS空间分析时,编写geoserver的扩展插件,插件连接PostGIS数据库,通过使用PostGIS空间分析功能,自己编写函数完成空间分析工作。

    基于geoserver的rest接口实现地图服务的自动发布。在ArcGIS Engine开发的桌面软件中,先将GIS数据导入到空间数据库,再调用geoserver的rest接口发布地图,最终实现的效果和ArcGIS发布地图的体验相同。

    搭建一个门户网站,内容包括帮助文档、地图资源、更新说明等,帮助文档以接口为线索,接口内部有接口说明和调用事例。地图资源中提供可以使用的所有地图,包括互联网地图和自己发布的业务地图,每个地图有个id,根据id就可以在API中轻松加载地图,不需要关心地图服务是如何发布的。

    将PostGIS、geoserver、tomcat全部修改为绿色版本,方便项目部署。

    经验总结

    1. 可以使用SLDEditor软件解决geoserver不容易配图的问题。geoserver的配图不好用,尝试了在QGIS上配图,然后发布到geoserver上,发现QGIS上配图后生成的sld样式文件格式,有很多geoserver都不支持,也不知道是版本没对应上还是其它原因,最后找了个开源工具SLDEditor来编辑样式文件,完美解决问题,但整体的使用体验跟ArcGIS差很多。
    2. PostGIS的空间分析功能很好用、很强大。所以空间分析功能,我们就主要用PostGIS来实现了,比如之前分享过的图形缓冲功能
    3. geoserver扩展更适合开发和生成地图服务相关的功能。GIS的空间分析功能一开始通过geoserver扩展插件实现,后来发现扩展插件的后台程序主要作用是数据传输,最主要的分析环节是在空间数据库PostGIS中实现的,而geoserver的扩展开发环境比较复杂,不如自己写的java后台好维护。geoserver扩展的优势是可以直接调用geoserver的资源和功能,它更适合开发和生成地图服务相关的功能。

    第四版 2019年

    背景

    开发出第三版后,在部门中使用了一年多,整体反应良好,有一部分懂GIS的同事,之前使用的ArcGIS JS API,用了leaflet封装的上一版地图API后,他们的第一反应是这个好轻便,比ArcGIS JS API 小了好多,加载很快。

    上一版发布后,我们留意了用户的使用习惯,大家的使用习惯基本都是先看示例,找到示例后,会直接把代码拷走使用,当示例不完全满足要求时,再去翻看API说明,最好的情况就是示例代码注释完善,一眼就能看懂,拷过来就能用。

    当然在使用的过程中,也慢慢发现了一些问题。

    懂GIS的同事反馈最强烈的是,能不能把原生接口放开。有个同事,每次都会先自己在网上找leaflet代码,确认能实现了再来找我们,让我们添加这个功能,甚至把资料链接都发过来了,整的我们都挺不好意思的。如果能把leaflet原生接口放开,有很多工作他自己就能解决了。

    然后是我自己,当我需要研究一个新功能时,我的第一反应不是去用我自己封装的地图API,而是更愿意使用原生leaflet去写,因为是我觉得自己封装的地图API用起来不够灵活。

    天呐!!!

    这不就和第一版时,同事说:”你们自己开发的东西,自己都不用”,是一样一样的嘛,如果说第一版时还有Flex语言的因素,那这第三版从内到外都是JS写的,没什么好解释的,就是让人家说中了。

    我们平时的工作,除了封装地图API,我们还有其它工作要做,上一版中,感觉我们很大一部分精力被消耗在了封装基础功能这件事上,导致没有时间去研究高级地图功能。如果能把原始接口放开,基础功能就可以直接使用原生接口,我们就有更多时间去研究高级地图功能。

    能不能放开原生接口?

    要放开原生接口面临几个问题:

    1. 地图坐标偏移问题。
      之前通过封装,在对外接口和地图之间构建了一个坐标适配层,解决了坐标偏移问题。如果放开原生接口,就没有办法再使用这方式,需要想其它办法。
    2. 用户使用习惯问题。
      不懂GIS的用户会不会习惯了扁平化接口,放开后觉得原生接口不好理解?leaflet中点的写法是[纬度,经度],和平时使用的[经度,纬度]不同,会不会有人适应不了?
    3. 版本向前兼容问题。
      上一个版本中为了追求接口的极简性,简化了很多数据格式类型,如果放开原生接口后,还要兼容这些格式将会产生很大的工作量,而且后续每增加一个功能都要考虑兼容这类数据的问题。

    解决方案:

    1. 针对坐标偏移问题。

      有两个方案,一是给用户提供一个坐标转换的接口,用户自己来转换坐标,但这样对用户不友好。二是对互联网地图瓦片进行纠偏,让地图统一坐标,不再偏移,这是最理想的,但有技术难点。不过,我们最终还是攻克了技术难点,采用了第二种解决方案,详见:leaflet中如何优雅的解决百度、高德地图的偏移问题

    2. 针对用户习惯问题。

      为什么百度、高德的地图API并没有使用扁平化接口,大家也没有觉得它们难用?我们研究后得出的结论是:在接口没有特别复杂的前提下,地图API如果能做到:能解决用户问题,bug少,示例丰富,说明文档清晰,大家就会觉得好用。接口是否是扁平化其实不怎么重要。而且,leaflet的原生接口本身就已经非常简洁了,单从简洁性考虑的话,没必要再封装。

    3. 针对版本向前兼容问题。
      我们决定不对上一个版本兼容,让两个版本同时保留,慢慢过渡,新项目新产品推荐大家用这一版,老项目我们继续提供技术支持,但不再做功能升级。这样经过1、2年后,就能慢慢切换过来。事实证明这个决定是对的,现在已经过去2年多时间,部门里大部分系统都已经切换都了新版本。

    技术架构

    技术架构在上一版的基础上做如下调整:

    1. 放开leaflet原生接口,不再对接口进行封装,改为以插件形式进行功能扩展。
    2. 集成多种互联网地图资源,通过对瓦片纠偏的方式解决它们的坐标偏移问题,对外统一使用wgs84坐标。
    3. 帮助文档由接口为线索改成以示例为线索,示例中的注释保证完善清晰,将示例代码中用到的方法给出类参考链接。
    4. 将leaflet的类参考文档进行翻译,放到平台中。
    5. 当有新的功能需求时,简单的功能不封装,直接给出示例,复杂的功能再考虑封装到插件中。
    6. 和geoserver相关性不强的地图分析功能,迁移到java后台,geoserver中只保留和制图相关的功能。

    经验总结

    1. leaflet类参考的翻译工作没做好,一共没翻译几页,但奇怪的是大家从来没有抱怨过这个问题。后来观察发现,用户基本不看文档,更多的是看示例,示例没有的,会直接问我们,文档其实只有我们在看(捂脸)。
    2. 有人问问题时,尽量以示例的方式记录下来,后续大家在示例中能找到这个问题就不会再问了。
    3. 调用示例的名称目前是文字列表形式,文字毕竟有表达上的局限性,对比高德、百度地图API,他们在示例列表的前一步,用动图的方式直接展示示例的最终效果,这样更加直观容易理解。
    4. 要尽量通过工具,让平台的维护变得简单,太复杂自己就不爱维护,最后平台容易废掉。

    第五版 2021年

    背景

    这一版还没有完成,年前刚做完技术预研工作,这里先把整体的思路简单和大家分享一下。

    总的来说,上一版已经很好用了,现在已经很少有来自用户的负面反馈。产品还曾在大项目中作为技术中台,提供给其他公司使用,同样效果很好。

    但我们自己还是有追求的,和高德、百度地图API相比,我们在下面几点上还需要改进:

    1. 地图美观度问题。
      地图的底图目前都是使用互联网地图瓦片,叠加上业务数据后,会遮盖底图中的注记,业务数据间的注记也不容易实现自动避让,再加上没有好用的地图配图工具,导致地图在展示多样数据时就会显得很乱、很丑。
    2. 展示性能问题。
      地图有时需要一些动画效果,比如用动画效果表达管网中水的流动方向和快慢,在数据量较大时会出现明显的卡顿。
    3. 地图配图问题。
      geoserver配图不好用,这个问题前面已经提到过,虽然使用SLDEditor可以生成配色文件,但一次只能生成一个图层,没有办法整体预览,效率太低,体验太差。高德、百度地图的自定义地图配图工具就很好用,美工可以直接上手,眼馋很久了。

    这一版的目标是解决上面3个问题,并继续优化用户体验。

    技术选型

    前台改为使用mapboxgl,不再使用leaflet,原因有两个:

    1. 性能。leaflet的上限在10万左右(详见:leaflet如何加载10万数据),而mapboxgl基于webgl技术开发,最大数据量取决于显卡性能和网络传输速度,理想情况下可以轻松达到百万级别。
    2. 美观度。mapboxgl对矢量瓦片支持特别好,再结合maputnik可以轻松实现高德、百度地图的自定义地图功能。

    地图配图使用maputnik,业务数据使用geoserver发布矢量瓦片,正常maputnik是不支持geoserver发布的矢量瓦片的,不过我们已经把这个问题解决了,详见:如何让矢量瓦片配图神器maputnik支持 geoserver

    底图数据有两种方案:

    1. 继续使用互联网地图栅格瓦片,适合对底图数据准确性要求较高的情况。
    2. 在本地发布OSM矢量瓦片地图,目前网上没有可以直接使用的免费矢量瓦片资源,只能自己把OSM数据下载到本地自己发布。OSM地图在国内的数据质量比较差,如果你的业务对底图数据的准确性要求不高,对样式要求比较高,比如大屏展示系统,可以选用这个方案。具体方法详见: 如何实现OSM地图本地发布并自定义配图

    地图可视化效果使用deck.gl、L7来实现。

    经验总结

    1. 使用maputnik同时加载geoserver发布的业务图层和本地发布的OSM底图,可以实现业务数据和底图的深度融合,比如把业务数据中的河流放到底图道路图层的下方,并实现标签的自动避让功能,类似这样的体验还是非常爽的。
    2. OSM地图的合规性存疑,建议自行将中国的国界线校准一遍再用。

    后续展望

    1. 解决OSM地图数据量少的问题。

      第五版解决方案有一点不完美的地方是,OSM地图在国内的数据量较少,这也是在我年初的Flag中,想要通过机器学习自动提取建筑物轮廓的起因。

    2. 研究三维GIS。

      之前的工作一直是二维GIS,三维GIS的业务比较少,也研究了cesium、ArcGIS js API 4.x、three.js 等,并在项目上有过使用,但对三维GIS的整体理解还是不像二维GIS那样通透,不能像二维GIS那样信心十足的给出一个自己满意的开源解决方案,所以在这块儿需要继续努力。

    总结

    关于地图API

    在toG业务的公司中,想要通过开源GIS,打造一套在易用性上比肩高德、百度地图的API,需要注意以下几点:

    1. 解决地图资源问题。把网上的地图资源整理好,把项目上的业务地图资源整理好,对外让用户可以直接使用。
    2. 解决地图坐标系问题。要搞定互联网地图的偏移问题,和多种地图坐标间的转换问题,对外让用户只使用一种坐标。
    3. 基础地图功能通过用户教育的方式实现,也就是提供完善的调用示例和说明文档。高级地图功能通过封装插件的方式实现,这样可以避免随着时间的推延,核心API越来越冗余。
    4. 场景丰富的、可以直接使用的调用示例,比接口是否简介、文档是否详细更重要。
    5. 用户觉得地图API是否好用的影像因素,从高到低排序:能不能解决问题、有没有bug、有没有丰富的示例、技术支持是否到位、文档是否清晰。
    6. 己所不欲勿施于人。找一个真实的业务场景去使用自己的API,并不断的从中发现问题,解决问题,完善功能,直到自己觉得非常好用为止,这样可以强迫自己站在用户的角度去思考问题。如果自己都不愿意去用,那就肯定是不好用,最终不会走的长远。
    7. 要做好技术支持工作。开发地图API需要一个长期的、持续迭代的过程,这个过程中难免有这样那样的问题,如果你的用户支持好,它能帮你弥补那些问题给用户带来的不好体验。
    8. 学会通过工具提高平台维护效率。多去想想平台维护的过程中,哪些环节可以通过自动化或半自动的方式完成,比如生成文档环节、更新部署环节等,节省了时间好去研究更深层次的东西。

    关于开源GIS解决方案

    下面是我推荐的两种组合方案,其实都是前面提到过的,这里汇总一下。

    轻量版:leaflet + geoserver + postGIS

    这个组合网上的资料多,软件简单易用,普适性强,能满足绝大多数人对二维GIS的需求。

    矢量瓦片版:mapboxgl + maputnik + geoserver + postGIS + openmaptiles + three.js

    这个组合可以搭建出一套类似高德、百度地图的自定义地图,也可以实现白模三维地图,如果你比较看重地图可视化效果,那么推荐你使用这一套。



    好了,就到这里吧。如果觉得对你有帮助,可以通过持续关注和多多分享来支持我们。


    原文地址:http://gisarmory.xyz/blog/index.html?blog=GISerSolution

  • 相关阅读:
    昨天晚上简单英文词典查询及排版系统写完了
    c函数 atoi() 将字符串转换为整型 kbhit() 检测是否有按键按下 区分bioskey()
    写了一个字典树
    用scanf清空缓冲区 对比fflush
    爬取千千小说 -- xpath
    第二十六篇 -- 去掉标题栏并自定义标题栏
    git clone 中途停止不动
    使用turtle库画一朵玫瑰花带文字
    正则爬取我要个性网的头像
    用Pygal画一个英雄能力的图
  • 原文地址:https://www.cnblogs.com/lihaijia/p/14604158.html
Copyright © 2020-2023  润新知