首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

无代码编程时代下,程序员要失业了?

  • 24-03-05 03:41
  • 2382
  • 13306
blog.csdn.net

640?wx_fmt=gif

640?wx_fmt=jpeg

作者 | Phodal

责编 | 伍杏玲

【程序人生 编者按】“中台之后,便是无代码编程。”无代码编程是什么?开发流程是怎么样的?有何优缺点?

无代码编程时代来了,就不需要程序员编写代码了吗?下面作者将跟大家聊聊无代码编程的那些事儿。

规模化的组织,经常要面临这样的挑战:每个应用的基础设施是相同的,部分的代码也是相同的,甚至于它们可能只是数据模型不同而已。结果却导致了,他/她们要一次又一次地重新编写一个应用。对于一个新的应用而言,它需要对接大量的三方(非自己团队)服务。服务之间的不断变化 ,导致了对应的使用方也需要发生变化。不断变化的业务,导致了前台的设计不断变化。为了应对快速谈的的前台服务,后台便诞生了中台,以提供快速的响应能力。而随着中台进一步沉淀,从某种形式上趋于稳定,而前台仍然需要快速地响应能力。

于是乎,作为一个前端开发人员,我们不断提炼和复用代码,想着的模式在之前的文章已提到了:

  • 脚手架

  • 组件库

  • 模式库

  • 模板和模板应用

对应的,我们还创建了一系列的 CLI、工具集、编程器插件以及设计系统,以完成整个系统的快速开发。然而,我们还缺少一套有效的工具,来统一化的管理这些工具。

换句话来说,就是:我们需要一个前端的中台,它便是无代码/低代码编程。

 

 

640?wx_fmt=png

什么是无代码编程?

 

无代码/低代码是一种创建应用的方法,它可以让开发人员使用最少的编码知识,来快速开发应用程序。它可以在图形界面中,使用可视化建模的方式,来组装和配置应用程序。开发人员可以直接跳过所有的基础架构,只关注于使用代码来实现业务逻辑。

当然,从开发人员的角度来看,降低代码量,可能是:

  1. 框架本身处理了复杂性。毕竟 “复杂度同力一样不会消失,也不会凭空产生,它总是从一个物体转移到另一个物体或一种形式转为另一种形式。”

  2. 代码生成减少了工作量。大量的复制、粘贴需要更多的时间。

流程

只是凭借这个概念,我们是无法理解无代码编程的。于是,我画了一张图来展示相应的架构和流程:

640?wx_fmt=jpeg

低代码编程流

依照我的观点来看,我将无代码编程分为了两部分:

  • 用于构建 UI 的编辑器——一种在线的拖拽式 UI 设计和页面构建工具

  • 用于编写业务逻辑的流编辑器——通过流编程的方式来编写业务代码(多数是对于数据的处理)

UI 编程器

为了设计出我们的 UI 构建器,我们需要准备好一系列的基础设施:

  • UI 编程器。用于拖拽式设计 UI。

  • 空白脚手架。一个带有完整的应用生命周期的项目,但是它是一个空白的项目——用于我们在构建 UI 的过程中,随时随地的添加组件和代码。

  • 设计系统。我们需要一个完整的组件库,大量的页面模板,以及一定数量的模板应用,减少相应的开发工具量。

  • 代码片段集。它将设计系统中的组件库进一步实例化成代码段,在完成编辑后通过 CLI 来动态编辑代码。

  • DSL(领域特定语言,可选)。中间生成物,用于隔离框架与设计。

流编程器

  • 流编程器。用于拖拽式、输入编写业务代码。

  • 后端服务。如果不能提供现成的后端服务,则需要拥有一个标准的 API 规范,以及相应的 mock server。

  • 模式库。包含相应的业务处理代码,如通用的登录、数据获取、UI 交互等。

  • DSL(领域特定语言,可选)。同上

当然了,我们还需要能实时预览构建出来的应用。随后,我们执行了构建,而后构建出了一个半成品应用。开发人员只需要在它的基础上开发应用即可。而在开发人员开发的过程中,我们可以设计一系列的工具,来帮助开发人员更快速地构建应用。

  • 编辑器插件。包含设计系统、模式库等的自动完成代码,以及组织内部常用的代码库。

  • 调试工具。对于混合类型的应用而言,我们还需要一个开发工具来快速构建应用。

从上述的流程上来看,无代码编程还具有以下的特点:

  • 拖放式界面。又或者是可视化模型——基于节点和箭头。

  • 基于视觉的设计。

  • 可扩展的设计。如对于插件、插件商店,社区等一系列的支持。

  • 跨平台功能。支持 PC Web 应用开发,支持移动应用构架等。

  • 强大的部署后。即平台包含着整个应用的生命周期。

  • 拥有丰富的集成支持。可以随意的找到需要的组件,以及对应的后台服务。

  • 配置化。它也意味着大量的自定义配置。

  • 自制的领域特定语言(可选)。用于构建时优化。

优缺点

相应的,它具有以下的一些优点:

  1. 高效。不用多说,节省时间和开发成本。

  2. 有限的 Bug,安全性。

  3. 低成本。其所需的预算非常有限。

  4. 易用(取决于设计)。

  5. 开发速度更快。

  6. 开发过程中的 AI 。

  7. 维护成本低。

对应的相应的缺点有:

  1. 仍然需要编程技能。

  2. 受限的自定义能力。

  3. 可扩展性成了新的问题。

  4. 集成受限。

就当前而言,低代码开发平台通常分为两大类:

  • 对于外部:制作简单的产品,如网络移动应用程序

  • 对于内部:为您的团队或企业创建业务应用程序

诸如只使用 CRUD、表单、验证、简单聚合、分页等简易的服务。最常见的例子就是表单构建了,诸如金数据这样的应用,便是可以直接通过拖拽元素来生成,相应的开源实现有 jQuery Form Builder。

对于开发人员来说,我们只需要定义好数据模型,再通过拖拽来决定元素的位置即可。从这种角度来看,只要能使用 Serverless 构建的应用和服务,都可以直接使用低代码开发模式。

 

640?wx_fmt=png

开发流程对比

 

从我们的理解来看,传统应用的开发流程是:

  1. 分析、设计、确认、规划需求。

  2. 设计系统架构。

  3. 搭建前后端项目。选择技术栈、从零开始搭建或者从脚手架中创建。

  4. 搭建持续集成。

  5. 创建线框图和高保真原型。

  6. 设计数据模型,定义前后端契约,即 API URI、方法、字段等。

  7. 前后端实现业务逻辑。

  8. 前端实现 UI 页面。

  9. 集成第三方后端服务。

  10. 功能需求测试(DEV、QA、ST、UAT)

  11. 跨功能需求测试(安全性、性能等)

  12. 部署到生产环境。

低代码开发流程:

  1. 分析、设计、确认、规划需求

  2. 选择需要的第三方 API

  3. 在可视 IDE 中绘制应用程序的工作流程、数据模型和用户界面。

  4. 连接 API——通常使用服务、函数发现。

  5. 编写业务逻辑(可选)。手动代码添加到前端或者自定义自动生成的 SQL 查询。

  6. 用户验收测试。

  7. 部署到生产环境。

从步骤上来看,无代码编程少了几个步骤。这些步骤都因为大量丰富的内部系统集成,而变得非常简单。

 

640?wx_fmt=png

适用场景

 

就当前而言,无代码编程实际上是一种高度的场景预设的模式。因此,它存在一定的适用场景:

  • 模型驱动开发。

  • 快速 UI 构建。

  • 极简的业务功能。

  • IT 资源受限。在资源受限的情况下,能快速开发出符合业务需求的应用最重要。

而从流程上来看,对于一部分的应用来说,我们并不能实现无代码编程——存在一些业务上的不同之处。因此,多数场景之下,只是实现了低代码编程。

若是想真实的无代码编程,则需要一些更特定的场景:

  • 设计表格(输入数据)

  • 创建报告(组织数据)

  • 常规调度和自动化过程(操纵数据)

更多的场景正在探索中。

 

640?wx_fmt=png

无代码编程的挑战

 

无代码编程,除了需要准备好上述的一系列基础设施,还不可避免地会遇到一系列挑战。

  1. 谁来写这部分代码?

  2. 客户端的基础设施准备。

  3. 服务端的服务平台搭建。

  4. 统一用户体验设计。设计出一系列能便利组合的组件,及对应的模板页面。与此同时,它们还能适应于不同的风格,即有多样性的主题支持。

  5. DevOps 流水线设计。低代码编程,依赖于一系列的自动化工具,以实现构建、调试、部署以及维护,同时还包含应用的测试。

  6. 领域语言设计。

  7. 自动化测试。如果我们的前端代码是自动生成的,那么我们还需要对它们进行测试吗?这是一个好问题,而如果代码是自动生成的,那么测试也应该是自动生成的。毕竟要在平台上,编写大量的自动化测试,以保证平台的质量。

其中,有一些部分略微复杂一些,我们大概可以探索一下。

 

640?wx_fmt=png

谁来写这部分代码?

 

在我们创建这样一个平台和工具时,我们要考虑的第一个问题是,这个工具是为谁写的?

  • 没有编程经验的人。如业务人员,他/她们对于业务系统有着非常丰富的经验。

  • 有编程知识,但是没有经验的人。

  • 有一定经验的开发人员。

  • 有丰富经验的开发人员。对于专业的人来说,自动化就意味着缺少灵活度。甚至与自己编写相比,他们要花费更多的时间来修复生成的代码。

显然,对于相当有经验的开发人员而言,这个工具并不一定是他们所需要的。

 

640?wx_fmt=png

客户端基础设施

 

从我的理解来看,它适合于快速的 MVP 构建,并且生成的代码还应该方便修改,而不是诸如早期的 DreamWeaver 或者 FrontPage 这样的工具。

而与此同时,由于面向的开发人员水平不同,我们所需要做的工具也不同:

  1. 支持云构建和调试。

  2. GUI 编程应用。

  3. 代码生成。

  4. 设计系统体系构建。组件库搭建,模板应用创建等。

更难的是,容易让开发人员能接受代码生成。

 

640?wx_fmt=png

服务端

 

对于一个低代码平台而言,它对应的后端应该:

  1. 大量可用的现有服务。身份验证、安全性、推送能力、地图等等。

  2. 快速构建出后端服务。若是有内部 Serverless 或者 FaaS 方案,可以说是再好不过了。

  3. 方便与第三方服务集成。

  4. 灵活性。支持多语言等。

统一的后端服务 API,对于后端服务来说,我们需要一个通用的范式。所有的 API 应该按照这样的范式来设计。不过,作为一个 API 的消费方,我们可能没有这么大的权力,但是我们可以采用装饰器模式,即封装第三方 API 成统一的方式。为此,我们采用的方式,仍然是:

  • 契约。诸如 Swagger UI,它可以直接创建一个简易可用的服务。

  • BFF。即我们一一去按我们的需要,去封装这些第三方应用。

  • 查询语言。与自己编写 BFF 相比,更简单的方式是采用:GraphQL 这样的后端查询语言,便捷性更高、模式更加灵活。

在开发前的设计期里,我们需要首先设计出对应的领域模型。

 

 

640?wx_fmt=png

领域语言设计

 

低代码环境使用(图形)建模语言来指定整个系统、产品的行为。它意味着:

  1. 将数据结构、领域模式应用到程序的各个层级中。

  2. 将业务规则、决策融入到应用中(层级)。

这也就意味着,我们需要设计一个模型语言。而它对于我们而言,实际上是领域特定语言(DSL)。如下是一个简单的 DSL 示例,用于描述使用到的组件:

 
 

{'style': '','id': 2,'blocks': 
    [{'content': {'content': 'content','title': 'hello'},'type':'card'}]
 }

除此,我们还需要设计对应的布局 DSL,诸如于:

 
 

H:[circle1(circle1.height)] // set aspect-ratio for circle1
HV:[circle2..5(circle1)] // use same width/height for other circles
H:|[circle1]-[circle2]-[circle3]-[circle4]-[circle5]|
V:|~[circle1..5]~| // center all circles vertically

最后,我们还需要将流代码,转换为真实的项目代码。三种类型的 DSL 结合下来,都不是一个轻松的工具。

 

640?wx_fmt=png

原型设计

 

写好现有的组件,通用型接口。如常见的登录接口,对于使用登录接口的业务来说,它们只关心三部分的内容:

  1. 成功登录。

  2. 取消登录。

  3. 登录失败。对于客户端而言,可以视为取消登录。对于服务端来说,则可能是密码错误、用户名不存在、账号被锁定等。

对应于以上情景,又有一些通用的逻辑处理:

  1. 登录成功。保存 Token,并返回历史页面。

  2. 登录失败。弹出一个自定义内容的提示框。

这些代码是相似的。

前端原型

在一些简单的前端应用里:

  • 模板。只是在使用这些模板,再为这些模板设置相应的属性,绑定对应的值。

  • 数据。其过程都只是在各种保存变量的值,并 CRUD 这些变量的路上。为此,我们需要一个数据处理的管道架构设计,用于处理这些值。

  • 函数。事实上,我们的所有函数都只是一些管理函数,只用于处理这些对应的逻辑。

这些常见的功能都可以做成一些组件,它们对于某些应用来说,代码相应的重复。

  • 无限加载页面。只需要绑定上 API,再控制一下元素的显示与隐藏即可。

  • 表单。定义好字段即类型,对应的前后台逻辑都有了。除此,我们还需要为它们自定义好常见的规则,如正则表达式。而一旦表单之间有联动,那么这个组件的设计就更加麻烦了。

  • 卡片式元素。

  • 表单和表格展示。

  • 常见图表。事实上,已经有一系列的图表工具了,我们只是在它们在基础上,进行了二次封装而已——使得它们可以变成领域语言的形式。

  • 高级的响应式布局。与每个应用独立开发布局不同的是,低代码平台需要提供一套强大的自定义、响应式布局设计——即要满足移动端的通用模式,还要满足桌面版的通用模式。如对于大量数据来说,桌面端使用的是 Pagination,移动端使用的是无限滚动。

后端原型

事实上,对于后端来说,低成本平台意味着,代码生成及服务生成。而服务本身是有限的,既然是业务上发生了一些变化,后端服务也可能并不会发生变化。

它也意味着:

  • 微服务化。每个后端服务要尽可能的小。

  • API 规范化。即采用统一的 API 格式,接受统一的权限管理

  • 大量的 API 服务。

  • 快速集成第三方服务方案。集成第三方服务是开发应用不可避免的情况。为了应对这个问题,我们需要做准备好对应的创建服务逻辑,传入第三方服务所需要的参数,便可以直接生成我们的转发服务。

那么,问题来了,既然如此,我们是否能提供一个定制的工具呢?让每个人可以创建自己的组件流?

答案,显然是可以的。

 

640?wx_fmt=png

概念证明

 

于是乎,在我最近设计的 PoC (概念证明)里,采用的是 Anguar 框架。相应的基本思想如下:

  1. 使用 Material Design 作为组件库,使用 CDK 的 Drag 来实现拖拽功能。每个拖拽的组件,称为 Element(元素),由 ElementDispatcher 由根据数据生成对应的组件。可拖拽的部分由两部分组成:布局 + 元素。

  2. UI 构建完后,生成对应的 DSL,目前采用的是 JSON。毕竟数据结构是最简单的领域特定语言。

  3. 借由 Angular Schematics 解析这部分的 DSL,来生成相应的项目代码。

 

相关开源项目:

拖拽式 Web 建造工具:https://grapesjs.com/

基于 Flow 的编程工具:https://noflojs.org/

DSL 布局生成器:https://github.com/ijzerenhein/autolayout.js/

可视化数据流编辑器:https://github.com/Gregwar/blocks.js

基于 React 的内容生成器:https://github.com/vigetlabs/colonel-kurtz

参考资料:

https://www.process.st/low-code/

https://medium.com/@stefan.dreverman/decisions-to-take-for-your-low-code-architecture-c0768b606f

https://medium.com/@stefan.dreverman/analyzing-coinmarketcap-data-with-neo4j-4930ba0068e1

https://www.outsystems.com/blog/what-is-low-code.html

https://medium.com/@stefan.dreverman/starting-a-low-code-application-architecture-13170fcd6fc7

https://www.quora.com/What-is-low-code 

作者简介:黄峰达(Phodal),ThoughtWorks Senior Consultant,CSDN 博客专家。长期活跃于 GitHub、CSDN,专注于物联网和前端领域。出版著作《自己动手设计物联网》,以及《Growth:全栈增长工程师指南》等六本电子书,并译有《物联网实战指南》。

本文经授权转自作者公众号「Phodal」。

 

学了两年Python没抓住重点只学了皮毛,到底错在哪?

https://edu.csdn.net/topic/python115?utm_source=cxrs_bw

640?wx_fmt=png

640?wx_fmt=jpeg

640?wx_fmt=gif

 热 文 推 荐 

☞ 微信表情暂停使用,漂流瓶功能彻底再见!

☞ 程序员找工作黑名单:除了 996.ICU,程序员还将如何自救?

☞ 为什么优秀的程序员都写博客?

☞ 30 位 90 后霸榜:名企 CEO、10 亿身家,马云的接班人?

☞ 贾跃亭吹过的牛,苹果来实现?

☞ 何恺明的GN之后,权重标准化新方法能超越GN、BN吗? | 技术头条

☞ 2019年关于VM和Kubernetes的思考 | 技术头条

☞ “这跟香蕉吃不吃?别墨迹!”来自密码极客的无奈与怒吼…

☞ 刺激!我31岁敲代码10年,明天退休!

 

System.out.println("点个在看吧!");
console.log("点个在看吧!");
print("点个在看吧!");
printf("点个在看吧!\n");
cout << "点个在看吧!" << endl;
Console.WriteLine("点个在看吧!");
Response.Write("点个在看吧!");
alert("点个在看吧!")
echo "点个在看吧!"

640?wx_fmt=gif点击阅读原文,输入关键词,即可搜索您想要的 CSDN 文章。

640?wx_fmt=png你点的每个“在看”,我都认真当成了喜欢

程序人生
微信公众号
笑谈开发轶事,品味程序人生。

目录

前言

一、关于KMZ和KML

1、KMZ是什么

2、KML是什么

二、Java解析实例

1、POM.xml引用

2、KML 基类定义

3、空间对象的定义

4、Kml解析工具类

三、KML文件的解析

1、KML解析测试

2、KMZ解析测试

四、总结


前言

        今天是六.一儿童节,在这里祝各位大朋友们儿童节快乐,同时祝祖国的所有花朵们更加快乐。童年的欢乐可以治愈一切。不知道各位的儿童节是怎么度过的呀,是在陪孩子呢,还在在享受自己的欢乐时光,只愿大家都欢乐就好。这是写在最前面祝福的话,愿我们都开心快乐。

        最近碰到有朋友咨询,大致的问题是,他在项目中要实现KMZ数据的解析和WebGIS的可视化。刚好他用的技术栈是Java,同时KMZ的解析在各个网站上的相关解析代码不多。有很多代码是解析KML的,但是解析KMZ的相对比较少。一时没有参考的例子,希望能结合JAVA讲一下如何进行KMZ数据的解析。其实话说回来,虽然大致了解KMZ是什么数据,但是在之前的项目过程中,接触的空间数据也基本都是shp、gdb等等,至于google的KMZ还真的是第一回接触。

        本文主要讲解如何用JAVA语言,直接解析KMZ数据。文章首先介绍google地图中的KMZ和KML数据,然后使用代码的方式实现数据的解析,最后展示解析成果以及如何将数据转换成空间WKT数据。关于JAVA解析KML的博客和资料有不少,但是KMZ文件的还是比较稀少的,供各位朋友在工作中解析KMZ文件有一个参考。

一、关于KMZ和KML

        在进行相关文件的解析之前,首先我们来看一下KMZ和KML这两种文件,先了解这两种文件是什么?用来做什么的,具体的文件内容是什么样的。本节主要提供这些基础知识的讲解。

1、KMZ是什么

        KMZ 文件包含主 KML 文件以及0个或多个用 ZIP 格式打包成一个单元的支持文件(称为归档)。然后,KMZ 文件就可以作为单个实体进行存储和通过电子邮件发送。NetworkLink 可从网络服务器提取 KMZ 文件。将 KMZ 文件解压缩后,主 .kml 文件及其支持文件便分离成其各自的原始格式和目录结构,以及原始文件名和扩展名。除了变成归档格式外,ZIP 格式也会受到压缩,因此归档只能包含一个大型 KML 文件。根据 KML 文件的内容,此过程通常会产生10:1的压缩。10千字节的 KML 文件可以用1千字节的 KMZ 文件来提供。

        KMZ是Google Earth默认的输出文件格式,是一个经过ZIP格式压缩过的KML文件,当我们从网站上下载KMZ文件的时候,Windows会把KMZ文件认成ZIP文件,所以另存的时候文件后缀会被改成.ZIP,因此需要手动将文件后缀改成.KMZ。   KMZ文件用ZIP工具软件打开,然后解压缩即可得到原始KML文件。当然,KMZ文件也有自己的好处,就是KMZ文件的自身可以包含影像,这样就可以不依赖引用网络上的截图。   一般情况下,双击KMZ/KML文件即可从Google Earth中打开地标文件,但是需要注意的是,KMZ/KML地标文件名不能包含中文字符,文件存放的路径也不能有中文字符,否则将无法在Google Earth中打开。

        这里我们以漂亮国的全球基地为说明,验证一下上述的内容。把KMZ文件的后缀名修改为zip,然后用压缩文件打开。可以看到以下的文件:

        总结一下,KMZ就是把KML文件,进行了一个打包。这个很重要,在后面的解析过程中,会用到这个知识点。讲完了KMZ,下面介绍一下KML。

2、KML是什么

        KML 代表 钥匙孔标记语言。此 GIS 格式基于 XML,主要用于 Google 地球。KML由Keyhole Inc开发,后来被Google收购.KMZ(KML-Zipped)取代KML成为默认的Google地球地理空间格式,因为它是文件的压缩版本。KML/KMZ于2008年成为开放地理空间联盟的国际标准。经度和纬度分量(十进制度)由 1984 年世界大地测量系统 (WGS84) 定义。垂直分量(高度)以米为单位从 WGS84 EGM96 大地水准面垂直基准面开始测量。

        KML (keyhole markup language)是以XML语言为基础开发的一种文件格式,用来描述和存储地理信息数据(点、线、面、图片等),是纯粹的xml文本格式,可用记事本打开编辑,所以kml文件很小。KML跟XML文件最大的不同就是KML描述的是地理信息数据。最早开发KML的是keyhole公司,2004年Goole收购keyhole并用KML开发GooleEarth。KML是原先的Keyhole客户端进行读写的文件格式,是一种XML描述语言,并且是文本格式,这种格式的文件对于Google Earth程序设计来说有极大的好处,程序员可以通过简单的几行代码读取出地标文件的内部信息,并且还可以通过程序自动生成KML文件,因此,使用KML格式的地标文件非常利于Google Earth应用程序的开发。

        这里我们还是以上面的kml文件为说明,将打开的示例xml文件内容展示如下:

  1. "1.0" encoding="UTF-8"?>
  2. <kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2">
  3. <Document>
  4. <Folder>
  5. <name>Point Featuresname>
  6. <description>Point Featuresdescription>
  7. <Placemark>
  8. <description>Airportdescription>
  9. <name>name>
  10. <Point>
  11. <coordinates>-80.0408900000,32.8985600000,0coordinates>
  12. Point>
  13. Placemark>
  14. <Placemark>
  15. <description>Airportdescription>
  16. <name>name>
  17. <Point>
  18. <coordinates>-110.8822600000,32.1652200000,0coordinates>
  19. Point>
  20. Placemark>
  21. Folder>
  22. Document>
  23. kml>

        以上就是一个KML文件的示例,其主体内容就是一个XML。它以XML为主体,用来存储地理空间数据。因此对KML数据的解析,其本质就是对XML文件的解析。

二、Java解析实例

        本节主要以代码实战的方式介绍使用Java编程语言实现对KML语言和KMZ语言的解析。由于涉及到xml的解析,这里不采用最原始的dom解析方式。对于KML语言,有成熟的组件de.micromata.jak.JavaAPIforKml对KML的解析。这里对相关的解析组件进行介绍:

序号组件名称作用
1de.micromata.jak.JavaAPIforKmlKML文件解析
2org.apache.commons.commons-compress压缩包解压
3com.vividsolutions.jtsJTS wkt字符串构建

1、POM.xml引用

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0modelVersion>
  5. <groupId>com.yelanggroupId>
  6. <artifactId>gdal_demo1artifactId>
  7. <version>0.0.1-SNAPSHOTversion>
  8. <name>gdal_demo1name>
  9. <description>试验description>
  10. <properties>
  11. <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
  12. <maven.compiler.source>1.8maven.compiler.source>
  13. <maven.compiler.target>1.8maven.compiler.target>
  14. properties>
  15. <dependencies>
  16. <dependency>
  17. <groupId>de.micromata.jakgroupId>
  18. <artifactId>JavaAPIforKmlartifactId>
  19. <version>2.2.1version>
  20. dependency>
  21. <dependency>
  22. <groupId>org.apache.commonsgroupId>
  23. <artifactId>commons-compressartifactId>
  24. <version>1.21version>
  25. dependency>
  26. <dependency>
  27. <groupId>com.vividsolutionsgroupId>
  28. <artifactId>jtsartifactId>
  29. <version>1.13version>
  30. dependency>
  31. dependencies>
  32. project>

2、KML 基类定义

        这里进行kml 基类定义,将name、description、List进行统一封装。针对Point、Polygon、Polyline对象,在自己的对象属性中扩展额外的属性。这里采用OOP的实现方式。网上很多的代码没有将父级类抽象出来,同时其代码只解析了name。没有解析description属性。这里我们将描述信息同样解析出来。关键代码如下:

  1. package com.yelang.kmzcase;
  2. import java.util.List;
  3. import de.micromata.opengis.kml.v_2_2_0.Coordinate;
  4. /**
  5. * kml 基类,将name、description、List进行统一封装
  6. * @author 夜郎king
  7. */
  8. public class KmlBaseEntity {
  9. private List points;
  10. private String name;
  11. private String description;
  12. public List getPoints() {
  13. return points;
  14. }
  15. public void setPoints(List points) {
  16. this.points = points;
  17. }
  18. public String getName() {
  19. return name;
  20. }
  21. public void setName(String name) {
  22. this.name = name;
  23. }
  24. public String getDescription() {
  25. return description;
  26. }
  27. public void setDescription(String description) {
  28. this.description = description;
  29. }
  30. public KmlBaseEntity(List points, String name, String description) {
  31. super();
  32. this.points = points;
  33. this.name = name;
  34. this.description = description;
  35. }
  36. public KmlBaseEntity() {
  37. super();
  38. }
  39. }

3、空间对象的定义

        空间对象常见的类型包括点(Point)、线(Polyline)、面(Polygon)三种类型。这里我们将根据需要定义不同的空间对象。下面分别给出实例代码:

KmlPoint.java

  1. package com.yelang.kmzcase;
  2. import java.util.List;
  3. import de.micromata.opengis.kml.v_2_2_0.Coordinate;
  4. public class KmlPoint extends KmlBaseEntity{
  5. private String color;
  6. public String getColor() {
  7. return color;
  8. }
  9. public void setColor(String color) {
  10. this.color = color;
  11. }
  12. public KmlPoint(List points,String name,String description,String color){
  13. super(points, name, description);
  14. this.color = color;
  15. }
  16. public KmlPoint() {
  17. super();
  18. }
  19. }

KmlLine.java 

  1. package com.yelang.kmzcase;
  2. public class KmlLine extends KmlBaseEntity {
  3. private String color;
  4. private long width;
  5. public String getColor() {
  6. return color;
  7. }
  8. public void setColor(String color) {
  9. this.color = color;
  10. }
  11. public long getWidth() {
  12. return width;
  13. }
  14. public void setWidth(long width) {
  15. this.width = width;
  16. }
  17. }

KmlPolygon.java

  1. package com.yelang.kmzcase;
  2. /**
  3. * @program: 面状实体
  4. **/
  5. public class KmlPolygon extends KmlBaseEntity {
  6. private String color;
  7. public String getColor() {
  8. return color;
  9. }
  10. public void setColor(String color) {
  11. this.color = color;
  12. }
  13. }

4、Kml解析工具类

        这里定义Kml的解析工具类,主要负责解析KML,然后根据不同的图层,将属性和空间坐标点信息赋值给不同的空间数据集合。网上有一些解析的代码,仅解析name属性。这里扩展了其它的属性,包括描述属性。详细代码如下:

  1. package com.yelang.kmzcase;
  2. import de.micromata.opengis.kml.v_2_2_0.*;
  3. import java.io.File;
  4. import java.io.InputStream;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. /**
  8. * @description: KML文件解析
  9. **/
  10. public class ParsingKmlUtil {
  11. /**
  12. * 解析kml文件
  13. */
  14. public KmlData parseKmlByFile(File file) {
  15. Kml kml = Kml.unmarshal(file);
  16. return getByKml(kml);
  17. }
  18. /**
  19. * 解析kml文件流
  20. *
  21. * @param inputstream
  22. * @return
  23. */
  24. public KmlData parseKmlByInputstream(InputStream inputstream) {
  25. Kml kml = Kml.unmarshal(inputstream);
  26. return getByKml(kml);
  27. }
  28. /**
  29. * Kml对象转自定义存储对象
  30. *
  31. * @param kml
  32. * @return
  33. */
  34. public KmlData getByKml(Kml kml) {
  35. KmlData kmlData = new KmlData();
  36. kmlData.setKmlPoints(new ArrayList<>());
  37. kmlData.setKmlLines(new ArrayList<>());
  38. kmlData.setKmlPolygons(new ArrayList<>());
  39. Feature feature = kml.getFeature();
  40. parseFeature(feature, kmlData);
  41. return kmlData;
  42. }
  43. /**
  44. * 解析kml节点
  45. * @param feature
  46. * @param kmlData
  47. */
  48. private void parseFeature(Feature feature, KmlData kmlData) {
  49. if (feature != null) {
  50. if (feature instanceof Document) {
  51. List featureList = ((Document) feature).getFeature();
  52. featureList.forEach(documentFeature -> {
  53. if (documentFeature instanceof Placemark) {
  54. getPlaceMark((Placemark) documentFeature, kmlData);
  55. } else {
  56. parseFeature(documentFeature, kmlData);
  57. }
  58. });
  59. } else if (feature instanceof Folder) {
  60. List featureList = ((Folder) feature).getFeature();
  61. featureList.forEach(documentFeature -> {
  62. if (documentFeature instanceof Placemark) {
  63. getPlaceMark((Placemark) documentFeature, kmlData);
  64. } else {
  65. parseFeature(documentFeature, kmlData);
  66. }
  67. });
  68. }
  69. }
  70. }
  71. private void getPlaceMark(Placemark placemark, KmlData kmlData) {
  72. Geometry geometry = placemark.getGeometry();
  73. /*String name = placemark.getName();
  74. placemark.getDescription();
  75. System.out.println(placemark.getDescription());
  76. if (name == null) {
  77. name = placemark.getDescription();
  78. }
  79. parseGeometry(name, geometry, kmlData);*/
  80. parseGeometry(placemark,geometry,kmlData);
  81. }
  82. /**
  83. * 解析点线面形状的数据分别放入存储对象
  84. * @param placemark placemark对象
  85. * @param geometry 形状类型
  86. * @param kmlData 存储对象
  87. */
  88. private void parseGeometry(Placemark placemark, Geometry geometry, KmlData kmlData) {
  89. if (geometry != null) {
  90. if (geometry instanceof Polygon) {
  91. Polygon polygon = (Polygon) geometry;
  92. Boundary outerBoundaryIs = polygon.getOuterBoundaryIs();
  93. if (outerBoundaryIs != null) {
  94. LinearRing linearRing = outerBoundaryIs.getLinearRing();
  95. if (linearRing != null) {
  96. List coordinates = linearRing.getCoordinates();
  97. if (coordinates != null) {
  98. outerBoundaryIs = ((Polygon) geometry).getOuterBoundaryIs();
  99. addPolygonToList(kmlData.getKmlPolygons(), placemark, outerBoundaryIs);
  100. }
  101. }
  102. }
  103. } else if (geometry instanceof LineString) {
  104. LineString lineString = (LineString) geometry;
  105. List coordinates = lineString.getCoordinates();
  106. if (coordinates != null) {
  107. coordinates = ((LineString) geometry).getCoordinates();
  108. addLineStringToList(kmlData.getKmlLines(), coordinates, placemark);
  109. }
  110. } else if (geometry instanceof Point) {
  111. Point point = (Point) geometry;
  112. List coordinates = point.getCoordinates();
  113. if (coordinates != null) {
  114. coordinates = ((Point) geometry).getCoordinates();
  115. addPointToList(kmlData.getKmlPoints(), coordinates, placemark);
  116. }
  117. } else if (geometry instanceof MultiGeometry) {
  118. List geometries = ((MultiGeometry) geometry).getGeometry();
  119. for (Geometry geometryToMult : geometries) {
  120. Boundary outerBoundaryIs;
  121. List coordinates;
  122. if (geometryToMult instanceof Point) {
  123. coordinates = ((Point) geometryToMult).getCoordinates();
  124. addPointToList(kmlData.getKmlPoints(), coordinates, placemark);
  125. } else if (geometryToMult instanceof LineString) {
  126. coordinates = ((LineString) geometryToMult).getCoordinates();
  127. addLineStringToList(kmlData.getKmlLines(), coordinates, placemark);
  128. } else if (geometryToMult instanceof Polygon) {
  129. outerBoundaryIs = ((Polygon) geometryToMult).getOuterBoundaryIs();
  130. addPolygonToList(kmlData.getKmlPolygons(), placemark, outerBoundaryIs);
  131. }
  132. }
  133. }
  134. }
  135. }
  136. /**
  137. * 保存面状数据
  138. *
  139. * @param kmlPolygonList 已有面状数据
  140. * @param placemark placemark对象
  141. * @param outerBoundaryIs 面状信息
  142. */
  143. private void addPolygonToList(List kmlPolygonList, Placemark placemark, Boundary outerBoundaryIs) {
  144. LinearRing linearRing = outerBoundaryIs.getLinearRing();// 面
  145. KmlPolygon kmlPolygon = new KmlPolygon();
  146. kmlPolygon.setPoints(linearRing.getCoordinates());
  147. kmlPolygon.setName(placemark.getName());
  148. kmlPolygon.setDescription(placemark.getDescription());
  149. kmlPolygonList.add(kmlPolygon);
  150. }
  151. /**
  152. * 保存线状数据
  153. *
  154. * @param kmlLineList 已有线状数据
  155. * @param coordinates 线状经纬度数据
  156. * @param name 线状名称
  157. */
  158. private void addLineStringToList(List kmlLineList, List coordinates, Placemark placemark) {
  159. KmlLine kmlLine = new KmlLine();
  160. kmlLine.setPoints(coordinates);
  161. kmlLine.setName(placemark.getName());
  162. kmlLine.setDescription(placemark.getDescription());
  163. kmlLineList.add(kmlLine);
  164. }
  165. /**
  166. * 保存点状数据
  167. *
  168. * @param kmlPointList 已有点状数据
  169. * @param coordinates 点状经纬度数据
  170. * @param name 点状名称
  171. */
  172. private void addPointToList(List kmlPointList, List coordinates, Placemark placemark) {
  173. KmlPoint kmlPoint = new KmlPoint();
  174. kmlPoint.setName(placemark.getName());
  175. kmlPoint.setDescription(placemark.getDescription());
  176. kmlPoint.setPoints(coordinates);
  177. kmlPointList.add(kmlPoint);
  178. }
  179. }

        在定义了上述的代码之后,基本就可以实现了纯Java对KML文件的解析。下一节将调用上面的代码进行相应文件的解析。

三、KML文件的解析

        本节将重点介绍如何解析KML文件。

1、KML解析测试

  1. package com.yelang.kmzcase;
  2. import org.apache.commons.compress.archivers.ArchiveEntry;
  3. import org.apache.commons.compress.archivers.ArchiveInputStream;
  4. import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
  5. import com.vividsolutions.jts.geom.GeometryFactory;
  6. import com.vividsolutions.jts.io.WKTWriter;
  7. import de.micromata.opengis.kml.v_2_2_0.Coordinate;
  8. import de.micromata.opengis.kml.v_2_2_0.Kml;
  9. import java.io.*;
  10. import com.vividsolutions.jts.geom.*;
  11. public class KMZParser {
  12. public static void parseKml() throws IOException {
  13. ParsingKmlUtil parsingKmlUtil = new ParsingKmlUtil();
  14. File file = new File("C:/BaiduDownload/美军基地-地图数据(kmz)/美空军基地 - 副本/US-AFB.KML"); // 文件地址自己修改
  15. KmlData kmlData = parsingKmlUtil.parseKmlByFile(file);
  16. // assert kmlData != null;
  17. if (kmlData.getKmlPoints().size() > 0) {
  18. for (KmlPoint k : kmlData.getKmlPoints()) {
  19. GeometryFactory geoFactory = new GeometryFactory();
  20. Coordinate coord = k.getPoints().get(0);
  21. com.vividsolutions.jts.geom.Coordinate jtCoord = new com.vividsolutions.jts.geom.Coordinate(coord.getLongitude(), coord.getLatitude(), coord.getAltitude());
  22. // 使用GeometryFactory创建一个点
  23. Geometry point = geoFactory.createPoint(jtCoord);
  24. WKTWriter writer = new WKTWriter();
  25. String wkt = writer.write(point);
  26. System.out.println(k.getPoints() + "\t"+ wkt +"\t"+ k.getDescription() + "\t " + k.getName());
  27. }
  28. }
  29. }
  30. // 使用示例
  31. public static void main(String[] args) throws IOException {
  32. KMZParser.parseKml();
  33. }
  34. }

        由于在XML中的坐标是一个数组,如果想把这些数据保存到空间数据库中,需要进行格式转换,比如从WKT字符串转为Geometry。当然,保存到空间数据库中,有很多种方法,这里我们介绍一种,基于JTS的方式构建WKT字符串,因为在MyBatis-Plus中,可以直接操作WKT字符串。将坐标转换WKT字符串的方法如下:

  1. com.vividsolutions.jts.geom.Coordinate jtCoord = new com.vividsolutions.jts.geom.Coordinate(coord.getLongitude(), coord.getLatitude(), coord.getAltitude());
  2. // 使用GeometryFactory创建一个点
  3. Geometry point = geoFactory.createPoint(jtCoord);
  4. WKTWriter writer = new WKTWriter();
  5. String wkt = writer.write(point);

        在控制台中执行以上方法可以看到以下信息的输出,说明KML文件解析成功。

  1. [-80.04089,32.89856] POINT (-80.04089 32.89856) Airport CHARLESTON AFB/INTL 查尔斯顿空军基地/国际机场
  2. [-110.88226,32.16522] POINT (-110.88226 32.16522) Airport DAVIS-MONTHAN AFB 戴维斯-蒙森空军基地
  3. [-110.34393,31.58844] POINT (-110.34393 31.58844) Airport LIBBY AAF/SIERRA VISTA MUN 利比空军基地/谢拉维斯塔
  4. [-98.49243,33.98621] POINT (-98.49243 33.98621) Airport SHEPPARD AFB/WICHITA FALLS MUN 谢泼德空军基地/威奇托福尔斯??
  5. [-72.52899,42.19849] POINT (-72.52899 42.19849) Airport WESTOVER ARB/METROPOLITAN 韦斯特欧弗空军基地
  6. [-84.04541,39.82544] POINT (-84.04541 39.82544) Airport WRIGHT-PATTERSON AFB 赖特-帕特森空军基地
  7. [-84.07013,39.80072] POINT (-84.07013 39.80072) Airport WRIGHT-PATTERSON AFB 赖特-帕特森空军基地

2、KMZ解析测试

        网上很多博客都只讲了如何解析KML,但是对于KMZ的解析介绍比较少,以JAVA为开发语言解析更少了。上面的方法也只实现了KML的解析,如果把文件换成KMZ,肯定会报错的。不信来试试。错误信息如下:

         如果发生了以上的异常,不要急。发生这个异常的原因其实在文章的开头就讲过了。如果看到这里前面没有印象的,可以翻到前面去看一下。还是简单说明一下吧,主要是KMZ是KML的压缩包,而以上代码是KML的解析,没有对KMZ进行解压。这里有两种方法来实现,第一种是将KMZ文件进行解压,然后对解压后的文件解析,肯定没问题。第二种是在压缩包中读取,然后对压缩流信息进行解析。第一种方式会增加不必要的脏文件,第二种则不会,因为原始文件只有一个。 下面我们采用第二种实现方式,首先来定义一个处理接口(必须要):

  1. package com.yelang.kmzcase;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. /**
  5. * kml转换类,用于实现kml的自定义识别与读取
  6. * @author 夜郎king
  7. */
  8. public interface IKMLParser {
  9. /**
  10. * @param kmlInputStream
  11. * @throws IOException
  12. */
  13. void parseKML(InputStream kmlInputStream) throws IOException;
  14. }

        然后再定义针对KMZ的内容解析代码(基于在线解压的方式),然后已输入流的方式完成内容解析,这个代码网上比较少,如果需要KMZ文件解析,可以作为参考:

  1. public static void parseKMZFile(File kmzFile, IKMLParser kmlParser) throws IOException {
  2. try (InputStream fileInputStream = new FileInputStream(kmzFile);
  3. ArchiveInputStream archiveInputStream = new ZipArchiveInputStream(fileInputStream)) {
  4. ArchiveEntry entry;
  5. while ((entry = archiveInputStream.getNextEntry()) != null) {
  6. String name = entry.getName();
  7. if (name.toLowerCase().endsWith(".kml") || name.toLowerCase().endsWith(".kmz")) {
  8. // 如果发现.kml或.kmz文件,可以将其内容读取出来并传递给KMLParser处理
  9. kmlParser.parseKML(archiveInputStream);
  10. }
  11. }
  12. }
  13. }
  14. public static void parseKmz() throws IOException {
  15. File kmzFile = new File("C:/BaiduDownload/美军基地-地图数据(kmz)/美国全球基地.kmz");
  16. KMZParser.parseKMZFile(kmzFile, new IKMLParser() {
  17. @Override
  18. public void parseKML(InputStream kmlInputStream) throws IOException {
  19. // 在这里实现你的KML解析逻辑
  20. // 例如,可以将读取的KML内容转换为字符串
  21. try (BufferedReader reader = new BufferedReader(new InputStreamReader(kmlInputStream))) {
  22. String line;
  23. StringBuffer xmlContent = new StringBuffer(1024);
  24. while ((line = reader.readLine()) != null) {
  25. // 处理每一行KML数据
  26. xmlContent.append(line);
  27. }
  28. // System.out.println(xmlContent);
  29. Kml kml = Kml.unmarshal(xmlContent.toString());
  30. ParsingKmlUtil pku = new ParsingKmlUtil();
  31. KmlData kmlData = pku.getByKml(kml);
  32. if (null != kmlData.getKmlPoints()&&kmlData.getKmlPoints().size() > 0) {
  33. for (KmlPoint kp : kmlData.getKmlPoints()) {
  34. GeometryFactory geoFactory = new GeometryFactory();
  35. Coordinate coord = kp.getPoints().get(0);
  36. com.vividsolutions.jts.geom.Coordinate jtCoord = new com.vividsolutions.jts.geom.Coordinate(coord.getLongitude(), coord.getLatitude(), coord.getAltitude());
  37. // 使用GeometryFactory创建一个点
  38. Geometry point = geoFactory.createPoint(jtCoord);
  39. WKTWriter writer = new WKTWriter();
  40. String wkt = writer.write(point);
  41. System.out.println(kp.getPoints() + "\t"+ wkt +"\t"+ kp.getDescription() + "\t " + kp.getName());
  42. }
  43. System.out.println("美军全球基地的数据是==>" + kmlData.getKmlPoints().size());
  44. }
  45. } catch (IOException e) {
  46. e.printStackTrace();
  47. }
  48. }
  49. });
  50. }

        上述代码的主要逻辑是,使用compress对KMZ文件进行在线解压,然后动态拼接KML内容,最后解析KML文件,然后提取空间信息。经过上述步骤,完成信息的解析。在运行以上的代码,发现KMZ文件已经成功解析。

        至此已经完成KMZ和KML文件的解析。 

四、总结

        以上就是本文的主要内容,本文主要讲解如何用JAVA语言,直接解析KMZ数据。文章首先介绍google地图中的KMZ和KML数据,然后使用代码的方式实现数据的解析,最后展示解析成果以及如何将数据转换成空间WKT数据。关于JAVA解析KML的博客和资料有不少,但是KMZ文件的还是比较稀少的,算是作为网文的一种补充,供各位朋友在工作中解析KMZ文件有一个参考。行文仓促,定有不足之处,欢迎各位朋友在评论区批评指正,不胜感激。

        博文在编写过程中参考了以下博文:

        1、什么是 KMZ 文件?。

        2、从事地信测绘必须知道的GIS常见数据格式类型,以及数据格式和扩展名汇总。

        3、地理信息地图标记KML与KMZ的区别。

        4、java 生成kml 文件。

        5、Java解析kml文件获取点/线段/多边形面状地图经纬度信息。

Java GIS技术交流合作
微信名片
注:本文转载自blog.csdn.net的CSDN 程序人生的文章"https://blog.csdn.net/csdnsevenn/article/details/89062474"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top