当前位置: 首页 > news >正文

使用Geotools实现将Shp矢量文件加载SLD并合并图例生成-以湖南省周边城市为例

目录

前言

一、技术实现简介

1、生成成果说明

2、生成流程图说明

二、具体生成实践

1、渲染地图

2、生成图例

3、合并图像及输出

三、总结


前言

        在当今数字化时代,地理信息系统(GIS)技术已成为城市管理、资源规划、环境监测等众多领域不可或缺的工具。GIS 制图作为该技术体系中的核心环节之一,旨在将复杂的地理空间数据以直观、清晰且易于理解的地图形式呈现出来,为决策者提供有力的可视化支持。在 GIS 制图过程中,矢量数据的可视化呈现占据着举足轻重的地位。Shapefile(Shp)格式作为矢量数据的常用存储形式,以其良好的兼容性和通用性,在地理信息数据的交换与共享中得到了广泛应用。然而,仅仅拥有矢量数据还远远不够,如何通过合适的样式定义(SLD)来准确、有效地表达数据所蕴含的地理信息,是实现高质量 GIS 制图的关键所在。

        当我们将 Shp 矢量文件与 SLD 文件相结合时,可以对矢量数据进行精细的符号化和样式设置,以突出不同类型地物的特征和空间分布规律。但在这个过程中,图例的生成与合并成为了制图工作中的一个重要且复杂的任务。以湖南省周边城市为例,该区域包含了众多城市及其丰富的地理要素,这些要素在矢量文件中以不同的几何形状和属性信息存储。当我们为这些要素应用多样化的 SLD 样式后,为了保证地图的清晰性和易读性,需要对图例进行合理合并。图例不仅仅是地图的附加工具,更是用户理解地图内容的关键依据。

        在传统的 GIS 制图工具中,图例的合并往往需要手动操作,不仅耗时费力,而且容易出现错误和遗漏。而借助 Geotools 这一开源的 Java GIS 工具库,我们可以探索一种自动化、高效化的方式来实现 Shp 矢量文件加载 SLD 并合并图例生成。Geotools 提供了丰富的数据处理、样式应用以及地图渲染功能,它允许开发者灵活地对地理空间数据进行操作。在本研究中,我们将深入挖掘 Geotools 的相关功能,从 Shp 矢量数据的读取与解析开始,逐步实现 SLD 样式的加载与应用,重点攻克图例合并的技术难题。

        通过这一过程,我们不仅能够提高 GIS 制图的效率和质量,还能为湖南省周边城市的地理信息展示提供一种新的技术手段。这将有助于相关领域人员更准确地分析城市间的空间关系、资源分布以及区域发展态势,为城市规划、交通布局、环境保护等实际应用提供更具价值的地图成果,推动 GIS 技术在区域地理研究与实践中的深入应用与发展。

一、技术实现简介

        本节将分两部分描述,首先对生成成果进行一个简要的说明,然后结合实例生成流程图进行详细说明,后续将按照生成流程进行实例生成。        

1、生成成果说明

        这里结合成果图来进行说明,成果地图从结构来说,分成左右两个部分。左边主要是地图成果视图,右边是根据左边的sld生成的图例文本,配合颜色输出的内容。最终两部分的图像信息再合并成一张完整的图片。

2、生成流程图说明

        完整的成果生成流程图包含以上8个步骤,第一步是使用Geotools的方式来加载SHP资源,第二步是加载已经生成好的SLD样式描述文件,第三步是创建地图的绘制对象,第四步是按照地图的宽高比来计算新的高度,第五步是执行成果的渲染,第六步是解析SLD并且生成图例图像资源,第七步是将地图主体和图例资源进行合并,第八步是将合并后的图像资源进行本地保存,完整的处理流程就是这样的。通过以上的流程就可以实现前面一节的成果。

二、具体生成实践

        本节将详细说明在Geotools中如何实现将地图主图和图例自动合并拼接,并输出为本地图像资源的具体过程。关于如何在Geotools中将Shp文件输出为图片和对图例的自动生成,在之前的系列博客中均有所涉及,不仅包含了详细的教程,也详细介绍了在生成的过程中可能遇到的问题以及如何去解决,本文不再详细的进行叙述,如果有不明白的可以在评论区留言。当然也可以在发私信,翻阅之前的历史博客也是一种比错的方式。本节只详细讲述三个方面的内容,第一个方面是如何进行地图的渲染,第二是如何生成图例,第三个是如何进行图像资源的合并。

1、渲染地图

        渲染地图主要包含流程图中的前三个步骤,包含第三个步骤。涉及Shp文件的读取、sld文件的加载,以及创建地图内容,关键代码如下:

 // 1. 创建示例数据
File shapefile = new File("F:/vector_data/2021年7月中国乡镇行政区划shp-(乡镇信息有点旧)/2021湖南省乡镇行政区划/湖南省/湖南省/湖南省_市界.shp");
// 创建数据存储
ShapefileDataStore shpDataStore = new ShapefileDataStore(shapefile.toURL());
//一定要设置字符集,否则地图无法渲染,且不报错
shpDataStore.setCharset(Charset.forName("GBK"));
SimpleFeatureSource featureSource = shpDataStore.getFeatureSource();
String xzsldPath = "F:/wzh_workspace_20210320/geotools-fx/src/main/resources/maps/hunan_range.sld";// 2、SLD的方式
Style style = null;
try {style = SldUtils.createStyleFromSld(xzsldPath);
} catch (Exception e) {
}
// 3. 创建地图内容
MapContent mapContent = new MapContent();
FeatureLayer layer = new FeatureLayer(featureSource, style);
mapContent.addLayer(layer);
ReferencedEnvelope bounds = layer.getBounds();

        在这个环节需要注意的是字符集编码的设置,请尽量按照字符编码的设置来进行,否则会碰到地图是空白的情况,主要就是SLD文件解析失败,导致样式解析不生效,进而导致图像无法渲染。 关于字符集编码也有多篇博客进行介绍,如果没有时间翻阅之前的博客,可以在Qgis当中直接打开shp文件并进行查看。

2、生成图例

        这里主要对应流程图中的4、5、6步骤,这几个步骤也是整个生成过程的核心部分。尤其是生成文字图例的资源部分,需要熟练掌握。业务代码如下:

// 4、计算地理宽高比
double aspectRatio = bounds.getWidth() / bounds.getHeight();
//根据比例计算新高度
MAP_HEIGHT = (int) Math.round(MAP_WIDTH / aspectRatio);
// 5. 渲染地图
BufferedImage mapImage = renderMap(mapContent);
// 6. 手动生成图例
BufferedImage legendImage = createManualLegend(style);

        第四个步骤比较简单,主要是为了保持跟原始对象的高度和宽度的比例,相关设置比较简单,不再进行赘述。第五步是渲染地图,也就是在这一步来进行左边的图像资源的渲染设置,代码如下:

private static BufferedImage renderMap(MapContent mapContent) throws IOException {StreamingRenderer renderer = new StreamingRenderer();renderer.setMapContent(mapContent);// 设置渲染范围(示例使用默认范围,实际应根据数据调整)ReferencedEnvelope bounds = mapContent.getViewport().getBounds();if (bounds.isEmpty()) {bounds = new ReferencedEnvelope(-180, 180, -90, 90, DefaultGeographicCRS.WGS84);}// 创建地图图像BufferedImage image = new BufferedImage(MAP_WIDTH, MAP_HEIGHT, BufferedImage.TYPE_INT_ARGB);Graphics2D g2d = image.createGraphics();try {// 设置背景g2d.setColor(Color.WHITE);g2d.fillRect(0, 0, MAP_WIDTH, MAP_HEIGHT);// 绘制地图执行渲染renderer.paint(g2d, new Rectangle(MAP_WIDTH, MAP_HEIGHT), bounds);// 执行渲染} finally {g2d.dispose();}return image;
}

        需要注意的是,在第五步这里要返回一个BufferedImage对象,这个对象将作为输入参数在后面的方法中传入。 接下来是第六步,根据sld来生成图例,这一步也是非常重要的,关键代码如下:

private static BufferedImage createManualLegend(Style style) {int iconSize = 12;BufferedImage legend = new BufferedImage(LEGEND_WIDTH, MAP_HEIGHT, BufferedImage.TYPE_INT_ARGB);Graphics2D g = legend.createGraphics();// 设置抗锯齿g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);//这是白色背景g.setColor(Color.WHITE);g.fillRect(0, 0, LEGEND_WIDTH, MAP_HEIGHT);// 绘制标题g.setColor(Color.BLACK);g.setFont(new Font("宋体", Font.BOLD, 14));// 遍历规则生成图例项//int y = 30;int y = MAP_HEIGHT - iconSize- PADDING ;for (FeatureTypeStyle fts : style.featureTypeStyles()) {for (Rule rule : fts.rules()) {if (rule.getDescription().getTitle() == null) continue;// 绘制图例图标BufferedImage symbol = renderSymbol(rule, iconSize);if (symbol != null) {g.drawImage(symbol, PADDING, y, null);}// 绘制文字标签g.drawString(rule.getDescription().getTitle().toString(), PADDING + iconSize + 5, y + iconSize/2 + 5);y -= iconSize + PADDING;}}g.drawString("图例", PADDING, y);g.dispose();return legend.getSubimage(0, 0, LEGEND_WIDTH, MAP_HEIGHT);
}

        同样的,这里也是返回一个BufferedImage对象。这个对象就是目标图像的右边数据源。

3、合并图像及输出

        这里主要对应流程图中的7、8两步,最关键的流程就是将前面渲染得到的左边部分和右边部分进行合并。由于前面生成的图片本身是带了高度和宽度的,因此在执行图像的合并的时候,就一定会遇到对应高度和宽度的重新计算,因此这也是进行图像合并的关键之所在。这里直接给出相应的调用代码:

// 7. 合并图像
BufferedImage combined = combineImages(mapImage,legendImage);
// 8. 保存结果
ImageIO.write(combined, "png", new File("D:/manual_legend_map0423.png"));
mapContent.dispose();
shpDataStore.dispose();
System.out.println("完成");

        其中合并图像的关键方法代码如下:

private static BufferedImage combineImages(BufferedImage mapImage, BufferedImage legendImage) {// 计算总尺寸int totalWidth = MAP_WIDTH + LEGEND_WIDTH;int totalHeight = Math.max(MAP_HEIGHT, legendImage.getHeight());BufferedImage combined = new BufferedImage(totalWidth, totalHeight, BufferedImage.TYPE_INT_ARGB);Graphics2D g = combined.createGraphics();try {// 绘制地图g.drawImage(mapImage, 0, 0, null);// 绘制图例到右侧int legendX = MAP_WIDTH;int legendY = (totalHeight - legendImage.getHeight()) / 2;g.drawImage(legendImage, legendX, legendY, null);} finally {g.dispose();}return combined;
}

         在执行绘制的时候,我们使用g.drawImage执行两次绘制,首先绘制左边的部分,然后再指定的宽度和高度上来绘制图例信息。最终就得到在最开时给大家看到的成果图。

三、总结

        以上就是本文的主要内容,在本研究中,我们将深入挖掘 Geotools 的相关功能,从 Shp 矢量数据的读取与解析开始,逐步实现 SLD 样式的加载与应用,重点攻克图例合并的技术难题。通过这一过程,我们不仅能够提高 GIS 制图的效率和质量,还能为湖南省周边城市的地理信息展示提供一种新的技术手段。这将有助于相关领域人员更准确地分析城市间的空间关系、资源分布以及区域发展态势,为城市规划、交通布局、环境保护等实际应用提供更具价值的地图成果,推动 GIS 技术在区域地理研究与实践中的深入应用与发展。行文仓促,定有不足之处,欢迎各位朋友在评论区批评指正,不胜感激。

相关文章:

  • 手写深拷贝函数
  • 【C语言-选择排序算法】实现对十个数进行排序
  • ubuntu24设置拼音输入法,解决chrome不能输入中文
  • 排序(C)
  • NLP高频面试题(五十二)——BERT 变体详解
  • Docker Python 官方镜像使用说明(TAG说明)
  • vim的.vimrc配置
  • 前端加密介绍与实战
  • 46. 全排列
  • Mysql之存储过程
  • 多源数据集成技术分析与应用实践探索
  • DeepSeek在物联网设备中的应用:通过轻量化模型实现本地化数据分析
  • 达妙电机CAN通信及实验
  • 努比亚Z70S Ultra 摄影师版将于4月28日发布,首发【光影大师990】传感器
  • GPLT-2025年第十届团体程序设计天梯赛总决赛题解(共计266分)
  • Go全栈_Golang、Gin实战、Gorm实战、Go_Socket、Redis、Elasticsearch、微服务、K8s、RabbitMQ全家桶
  • Laravel 自定义 Artisan 命令行
  • Qt案例 使用QFtpServerLib开源库实现Qt软件搭建FTP服务器,使用QFTP模块访问FTP服务器
  • TORL:解锁大模型推理新境界,强化学习与工具融合的创新变革
  • 第六章 QT基础:3、QT的打包和部署
  • 刺激视网膜可让人“看”到全新颜色
  • 乌代表团与美特使在伦敦举行会谈,双方同意继续对话
  • “下一个高增长市场,还是中国”,龚正市长会见参加上海车展的国际企业高管
  • 阻燃材料点火就着引发一场火灾,河北一企业的产品被指不达标且涉嫌欺诈
  • 印控克什米尔发生恐袭事件,外交部:中方反对一切形式的恐怖主义
  • 福特中国CFO:依然坚信中国市场,上海帮助公司吸引到人才