基于Java和GeoTools的Shapefile矢量数据缩略图生成实践

news2024/9/20 5:39:48

目录

前言

一、关于GeoTools的图片生成

1、关于GtRenderer

2、关于 图像生成架构

3、流式计算绘制

二、全球空间预览生成实战

1、pom.xml中关于图像生成依赖

2、样式设置及地图资源绑定

3、图片生成绘制

4、图片生成测试

三、成果验证

1、全球范围生成

2、我国的范围示意图

 3、日本范围生成

四、总结


前言

        在很多的面向WebGIS的应用开发过程中,我们通常会将空间地理数据,比如Shapefile等矢量数据或者Tiff等栅格数据进行发布到GIS服务器中。为了能直观的展示这些空间矢量数据或者栅格数据的空间范围及大致的数据边界,我们需要将服务化之后的数据进行缩略图的预览展示。如果大家熟悉GeoServer或者ArcgisServer的话,对这个需求一定不陌生。如果我们是自己发布的GIS服务,怎么提供这种类似的空间数据的预览服务呢?在不引入其它的外部服务的情况下。这是个值得思考的问题。

        本文即是在上述的需求场景下出现的。本文主要使用Java语言,讲解如何使用GeoTools这个组件来进行空间Shapefile数据转换成图片,从而实现服务缩略图的功能。文章通过实例的模式讲解预览图片的生成,对于在研究Java的服务预览图片生成的同学和朋友有一定的参考价值。 

一、关于GeoTools的图片生成

        关于GeoTools这个使用Java开发的地理开发组件,它提供了许多丰富的功能和生态来帮助我们实现图片的生成操作。关于GeoTools的整体架构和相关知识,我想在后面的章节中再慢慢介绍。今天先试用它的图形渲染功能来实现空间数据的可视化渲染。本节重点讲解Geotools当中的GTRenderer功能。

1、关于GtRenderer

        关于在GeoTools中使用图像渲染和生成的功能,我们会使用到GtRenderer这个组件。它在GeoTools的官网原文中的介绍如下:

GTRenderer renderer is the reason why you signed up for this whole GeoTools experience; you want to see a Map.

GTRenderer is actually an interface; currently there are two implementations:

2、关于 图像生成架构

        为了实现空间对象的生成,我们来看下它的后台使用类的生成关系。这个图可以参考管网给出的一个类关系图。

        当然,上图中的相关接口和类中,尤其是在实现类中,实现类的方法和属性的定义是非常多的。但是在上图中做了一定的处理,没有展示那么多的方法。这里,我们会对StreamIngRenderer这个类比官网多一些的详细说明。 详细说明为什么使用流式计算,在实际的绘图过程中,有哪些方法是比较重要的

        在面向对象的学习过程中,我们选择从接口或者抽象类,自顶向下的方式来研究相关的类。因此,在这里,我们首先对抽象的接口GTRenderer进行研究。GTRenderer的源码不多,主要定义的都是关于如何绘制的API,这里直接给出它的代码:


package org.geotools.renderer;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.util.Map;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.MapContent;
import org.locationtech.jts.geom.Envelope;

public interface GTRenderer {
    public void stopRendering();
    public void addRenderListener(RenderListener listener);
    public void removeRenderListener(RenderListener listener);
    public void setJava2DHints(RenderingHints hints);
    public RenderingHints getJava2DHints();
    public void setRendererHints(Map<?, ?> hints);
    public Map<Object, Object> getRendererHints();
    public void setMapContent(MapContent mapContent);
    public MapContent getMapContent();
    public void paint(Graphics2D graphics, Rectangle paintArea, AffineTransform worldToScreen);
    public void paint(Graphics2D graphics, Rectangle paintArea, Envelope mapArea);
    public void paint(Graphics2D graphics, Rectangle paintArea, ReferencedEnvelope mapArea);
    public void paint( Graphics2D graphics,Rectangle paintArea,Envelope mapArea,AffineTransform worldToScreen);
    public void paint(Graphics2D graphics, Rectangle paintArea,ReferencedEnvelope mapArea,AffineTransform worldToScreen);
}

        在上面的方法中,我已经把相关的注释全部删除,在这个渲染接口中其实比较重要的方法其实就那么几个,第一是设置RenderingHints,第二是设置MapContent对象,第三就是执行具体的绘制,即上面代码中的Paint方法,在上面的代码中,一共提供了5个重载的方法,在实际使用的时候,大家根据自己的需要来进行灵活的选择。

3、流式计算绘制

        在定义了图片绘制的接口之后,最重要的是要实现图片的写入,这里需要来看一下GTRenderer对象的子类,即:

        可以看到在StreamingRenderer中定义了非常多的属性以及方法来支撑图片的绘制。 接下来我们看下绘制的相关类,为什么渲染的速度这么快。

 /** The thread pool used to submit the painter workers. */
private ExecutorService threadPool;

private PainterThread painterThread;

        在代码中可以看到以上代码片段,在进行绘制的时候,内部会使用线程池的方式来实现,所以这也是为什么处理效率比较高效的原因。

ExecutorService localThreadPool = threadPool;
boolean localPool = false;
if (localThreadPool == null) {
     localThreadPool = Executors.newSingleThreadExecutor();
     localPool = true;
}
Future painterFuture = localThreadPool.submit(painterThread);
List<CompositingGroup> compositingGroups = null;

        从这个代码可以看出,这里的处理线程池,我们可以在外部传入一个指定的线程池来进行任务的处理。如果外部没有传入线程池,在内部也会创建出一个线程对象来,这样就保证了一定会有一个线程池来进行处理。可以看到,这里的线程池采用的是Future的方式,与我们常见的Thread的方式有所区别,请大家注意。

        下面给出一个官网提供的将shp文件写出到图片文件的java实例代码。

public void saveImage(final MapContent map, final String file, final int imageWidth) {

    GTRenderer renderer = new StreamingRenderer();
    renderer.setMapContent(map);

    Rectangle imageBounds = null;
    ReferencedEnvelope mapBounds = null;
    try {
        mapBounds = map.getMaxBounds();
        double heightToWidth = mapBounds.getSpan(1) / mapBounds.getSpan(0);
        imageBounds = new Rectangle(
                0, 0, imageWidth, (int) Math.round(imageWidth * heightToWidth));

    } catch (Exception e) {
        // failed to access map layers
        throw new RuntimeException(e);
    }

    BufferedImage image = new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_RGB);

    Graphics2D gr = image.createGraphics();
    gr.setPaint(Color.WHITE);
    gr.fill(imageBounds);

    try {
        renderer.paint(gr, imageBounds, mapBounds);
        File fileToSave = new File(file);
        ImageIO.write(image, "jpeg", fileToSave);

    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

二、全球空间预览生成实战

        在之前的博文中,我们讲解了如何使用Geotools来进行shapefile的空间数据的生成。这里还是以全球的矢量数据为例,结合sld空间样式控制。我们使用GeoTools来生成一张图片,使用上面讲到的GTRenderer对象来实现图片缩略图的生成。

1、pom.xml中关于图像生成依赖

        要想实现在Geotools中进行图像生成,需要引入相关的依赖包。这里给出依赖的maven配置,请注意,这是基本的pom.xml,其它的geotools的依赖,请自行引入。

<dependency>
	<groupId>org.geotools</groupId>
	<artifactId>gt-referencing</artifactId>
	<version>${geotools.version}</version>
</dependency>
<dependency>
	<groupId>org.geotools</groupId>
	<artifactId>gt-epsg-hsql</artifactId>
	<version>${geotools.version}</version>
</dependency>
<dependency>
	<groupId>org.geotools</groupId>
	<artifactId>gt-epsg-extension</artifactId>
	<version>${geotools.version}</version>
</dependency>

2、样式设置及地图资源绑定

        为了让地图更好看,需要我们对地图进行样式的设置,关于SLD的解析和使用,我们不再赘述,直接提供代码。大家可以直接拷贝过去使用。

private Style createStyleFromSld(String uri)
			throws XPathExpressionException, IOException, SAXException, ParserConfigurationException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document xmlDocument = db.parse(uri);

		XPath xPath = XPathFactory.newInstance().newXPath();
		String version = xPath.compile("/StyledLayerDescriptor/@version").evaluate(xmlDocument);
		Configuration sldConf;
		if (version != null && version.startsWith("1.1")) {
			sldConf = new org.geotools.sld.v1_1.SLDConfiguration();
		} else {
			sldConf = new org.geotools.sld.SLDConfiguration();
		}
		StyledLayerDescriptor sld = (StyledLayerDescriptor) new org.geotools.xsd.DOMParser(sldConf, xmlDocument)
				.parse();
		NamedLayer l = (NamedLayer) sld.getStyledLayers()[0];
		Style style = l.getStyles()[0];
		return style;
}

        加载样式之后,还需要将地图的样式和地图进行绑定。如下面的代码所示:

/**
* 添加shp文件
* 
* @param shpPath
*/
@SuppressWarnings("deprecation")
public void addShapeLayer(String shpPath, String sldPath) {
	try {
		File file = new File(shpPath);
		ShapefileDataStore shpDataStore = null;
		shpDataStore = new ShapefileDataStore(file.toURL());
		// 设置编码
		Charset charset = Charset.forName("GBK");
		shpDataStore.setCharset(charset);
		String typeName = shpDataStore.getTypeNames()[0];
		SimpleFeatureSource featureSource = null;
		featureSource = shpDataStore.getFeatureSource(typeName);
		// SLD的方式
		Style style = null;
		try {
			style = createStyleFromSld(sldPath);
		} catch (Exception e) {
		}
		Layer layer = new FeatureLayer(featureSource, style);
		map.addLayer(layer);
	} catch (Exception e) {
		e.printStackTrace();
	}
}

        上面两个方法都比较简单,而且在之前的博客中也曾经讲过,实现map的读取和样式的绑定。在介绍了上面的两个方法之后,我们来讲述如何进行图片的生成。

3、图片生成绘制

        在讲解图片的绘制时,有几个小小的知识点是需要讲述的。首先是参考坐标,我们在进行绘图时,需要获取系统的参考坐标,然后根据参考坐标,将需要绘制的地图范围进行设置,也就是相当于生成ReferencedEnvelope。然后Graphics这个java的2d图像生成对象将Map对象绘制成图片,从而实现缩略图的生成功能。

/**
* 根据四至、长、宽获取地图内容,并生成图片
* 
* @param paras
* @param imgPath
*/
public void getMapContent(Map<String, Object> paras, String imgPath) {
	try {
		double[] bbox = (double[]) paras.get("bbox");
		double x1 = bbox[0], y1 = bbox[1], x2 = bbox[2], y2 = bbox[3];
		int width = (int) paras.get("width"), height = (int) paras.get("height");
		// 设置输出范围
		CoordinateReferenceSystem crs = DefaultGeographicCRS.WGS84;
		// CoordinateReferenceSystem crs = CRS.decode("EPSG:4326");
		ReferencedEnvelope mapArea = new ReferencedEnvelope(x1, x2, y1, y2, crs);
		// 初始化渲染器
		StreamingRenderer sr = new StreamingRenderer();
		sr.setMapContent(map);
		// 初始化输出图像
		BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		Graphics g = bi.getGraphics();
		((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
					RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
		Rectangle rect = new Rectangle(0, 0, width, height);
		// 绘制地图
		sr.paint((Graphics2D) g, rect, mapArea);
		// 将BufferedImage变量写入文件中。
		ImageIO.write(bi, "png", new File(imgPath));
	} catch (Exception e) {
		e.printStackTrace();
	}
}

4、图片生成测试

        下面我们进行实际的图片生成测试,测试的代码如下:

public static void main(String[] args) {
		long start = System.currentTimeMillis();
		Shp2Image shp2img = new Shp2Image();
		String shpPath = "F:/wzh_workspace_20210320/geotools-fx/src/main/resources/maps/countries.shp";
		String sldPath = "F:/wzh_workspace_20210320/geotools-fx/src/main/resources/maps/countries1.1.0-2.sld";

		String imgPath = "D:/countries0819-美国.png";
		Map<String, Object> paras = new HashMap<String, Object>();
		double[] bbox_usa = new double[] { -145.40999, 9.93976, -65.062300,81.12722 };

		paras.put("bbox", bbox_usa);
		paras.put("width", 1020);
		paras.put("height", 800);

		shp2img.addShapeLayer(shpPath, sldPath);
		shp2img.getMapContent(paras, imgPath);
		System.out.println("缩略图生成完成,共耗时" + (System.currentTimeMillis() - start) + "ms");
}

        以上就是完成将shp数据转为图片的生成代码,下面我们就可以运行相应的程序来进行调用测试。请注意,这里的bbox是想要生成的区域的空间区域的BBOX,也就是最大矩形外包框的坐标位置。还有需要生成图片的高度和宽度,在指定好这些参数之后就可以调用相应的应用程序来生成。

三、成果验证

        下面来进行成果的验证,我们分别定义了以下的城市,首先是全世界的地图范围图片生成,然后是中国图片生成、日本及美国的地图范围生成。当然每个不同的城市的bbox是不一样的,还要传入不同的bbox值进行验证。

1、全球范围生成

        全球的bbox范围值如下:

 double[] bbox = new double[]
{-179.9999999999999716,-90.0000000188696276,180.0000000000000000,83.6274185352932022};

        接下来看一下全球的生成视图:

2、我国的范围示意图

         我国的经纬度位置范围大致是:

double[] bbox_china = new double[] { 73.409999999999716, -3.5300000188696276, 135.0623000000000000,53.6274185352932022 };

 3、日本范围生成

        日本的经纬度bbox范围如下:

double[] bbox_japan = new double[] { 122.409999999999716, 22.9300000188696276, 151.0623000000000000,47.1274185352932022 };

四、总结

        以上就是本文的主要内容,本文主要使用Java语言,讲解如何使用GeoTools这个组件来进行空间Shapefile数据转换成图片,从而实现服务缩略图的功能。文章通过实例的模式讲解预览图片的生成,对于在研究Java的服务预览图片生成的同学和朋友有一定的参考价值。 阅读本文,不仅可以学习到如何将shp数据转换成图片缩略图,还可以进一步复习geotools包的具体使用。当前,关于图像生成的很多资源不足或者介绍不够深入,这里将比较详细的解释相关知识,同时给出具体的实例。行文仓促,定有不当之处,恳请各位专家学者博友在评论区留下宝贵的意见,万分感激。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2056882.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

快速批量替换图片名称为指定名称(附代码)

目录 一、需求二、代码使用方法三、代码四、效果展示 一、需求 深度学习配对训练&#xff0c;有时配对图像的名称需要一致&#xff0c;这里写了一个脚本&#xff0c;快速批量替换图片名称中某些字符串。 二、代码使用方法 使用代码时需要修改的地方见下&#xff1a; 三、代码…

一文1600字从0到1JMeter全流程性能测试实战!

项目背景&#xff1a; 我们的平台为全国某行业监控平台&#xff0c;经过3轮功能测试、接口测试后&#xff0c;98%的问题已经关闭&#xff0c;决定对省平台向全国平台上传数据的接口进行性能测试。 01、测试步骤 1、编写性能测试方案 由于我是刚进入此项目组不久&#xff0c…

[HGAME 2023 week1]Classic Childhood Game

方法一&#xff1a;在控制台输入mota&#xff08;&#xff09; 方法二&#xff1a;查看页面源码发现多个js,一一查看 在./Res/Events.js中发现编码&#xff08;准确来说是字符串&#xff09; \x59\x55\x64\x6b\x61\x47\x4a\x58\x56\x6a\x64\x61\x62\x46\x5a\x31\x59\x6d\x35\x7…

基于MATLAB视觉的静态手势识别系统

一、课题介绍及思路 为了丰富手势识别方法的多样性&#xff0c;提高手势识别的正确率&#xff0c;提出了一种基于手势轮廓像素变化的手势识别方法。在Matlab环境下&#xff0c;设计并开发了一个基于视觉的静态手势识别系统。系统主要由两部分组成&#xff1a;手势分割与手势识…

八股总结----数据库(MySQL和Redis)

1.MySQL部分 11.基本写法 列出数据库&#xff1a;show databases&#xff1b;创建数据库&#xff1a;create database mysql_test&#xff1b;切换数据库&#xff1a;use mysql_test&#xff1b;列出表&#xff1a;show tables&#xff1b;创建表&#xff1a;create table st…

回归预测|基于最大互信息与支持向量机结合的数据回归预测Matlab程序MIC-SVM 多特征输入单输出

回归预测|基于最大互信息与支持向量机结合的数据回归预测Matlab程序MIC-SVM 多特征输入单输出 文章目录 前言回归预测|基于最大互信息与支持向量机结合的数据回归预测Matlab程序MIC-SVM 多特征输入单输出 一、MIC-SVM模型最大信息系数&#xff08;MIC&#xff09;的原理支持向量…

网路安全-防火墙安全区域简介

文章目录 1. 概念介绍1.1 什么是安全区域&#xff1f;1.2 安全区域的分类1.3 安全区域级别1.4 安全区域的作用是什么&#xff1f; 2. 实战2.1 ENSP实验设计1实验目标&#xff1a;实验步骤&#xff1a; 2.2 ENSP实验设计2实验目标&#xff1a;实验步骤&#xff1a; 2.3 实验总结…

线性表【双向循环链表基本定义与操作】(带头结点)

1.双向循环链表的特征与图解 让头结点的 前驱指针 指向 链表的最后一个结点让 最后一个结点 的后继指针 指向 头结点。 2.双向循环链表的重要操作 1.双向循环链表的结构定义 双向循环链表的结构与双向链表完全一致&#xff0c;不同之处在于它的尾结点的next指针指向头结点&am…

【Docker深入浅出】【四】单体应用容器化与Dockerfile怎么写

文章目录 一. 应用的容器化——简介二. 单体应用容器化1. 获取代码与分析Dockfile2. 容器化当前应用&#xff08;构建具体的镜像&#xff09;3&#xff0e;推送镜像到仓库4. 运行应用程序5. 小结 三. 生产环境中的多阶段构建四. 应用容器化命令 本文介绍了如何容器化&#xff0…

springboot的学习(三):开发相关

简介 一些开发测试时用到的技术。 springboot 热部署 修改了代码&#xff0c;服务器不需要重启可以直接看到新的修改的效果。仅仅加载当前开发者自定义开发的资源&#xff0c;不加载jar资源。 在pom.xml配置文件中添加&#xff1a; <dependency><groupId>org.s…

AI可以写毕业论文吗?6款亲测好用人工智能写论文网站

AI写作工具在学术界的应用已经逐渐成为一种趋势&#xff0c;特别是在毕业论文的撰写过程中。这些工具不仅能够提高写作效率&#xff0c;还能帮助学生更好地组织和规划他们的研究内容。以下是六款经过亲测且好用的人工智能写论文网站推荐&#xff1a; 一、千笔-AIPassPaper 千笔…

【自动驾驶】控制算法(三)轮胎侧偏与车辆动力学模型

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

苍鹰来啦!快来看呀!NGO-BiTCN-BiGRU-Attention北方苍鹰算法优化多重双向深度学习回归预测

苍鹰来啦!快来看呀&#xff01;NGO-BiTCN-BiGRU-Attention北方苍鹰算法优化多重双向深度学习回归预测 目录 苍鹰来啦!快来看呀&#xff01;NGO-BiTCN-BiGRU-Attention北方苍鹰算法优化多重双向深度学习回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实…

Java实现MQTT通信(发布订阅消息)

文章目录 前言一、相关pom依赖二、相关代码1.MQTT工具类2.MQTT回调函数3.订阅消息4.发布消息 三、安装mosquitto1.mosquitto简介2.下载 四、安装MQTT.fx1.MQTT.fx简介2.下载3.使用 五、java订阅消息六、java发布消息 前言 MQTT是一种轻量级的物联网通信协议&#xff0c;基于客…

[Meachines] [Easy] Blue MS17-010永恒之蓝

信息收集 IP AddressOpening Ports10.10.10.40TCP:135/tcp msrpc, 139/tcp netbios-ssn, 445/tcp microsoft-ds, 49152/tcp msrpc, 49153/tcp msrpc, 49154/tcp msrpc, 49155/tcp msrpc, 49156/tcp msrpc, 49157/tcp msrpc $ nmap -p- 10.10.10.40 --min-rate 1000 -sC -sV …

YOLOV8 POSE姿态检测对图片绘制矩形和和关节点序号

代码如下 import cv2 import torchfrom ultralytics import YOLO# Load a model # model YOLO("yolov8n-pose.yaml") # build a new model from YAML model YOLO("yolov8n-pose.pt") # load a pretrained model (recommended for training) # model …

SQL— DQL语句学习【后端 11】

DQL语句 引言 DQL&#xff08;Data Query Language&#xff0c;即数据查询语言&#xff09;是SQL&#xff08;Structured Query Language&#xff09;中用于从数据库中检索数据的重要部分。在数据库管理中&#xff0c;DQL语句是日常工作中最常用的工具之一。通过DQL&#xff0…

leetcode-538. 把二叉搜索树转换为累加树

题目描述 给出二叉 搜索 树的根节点&#xff0c;该树的节点值各不相同&#xff0c;请你将其转换为累加树&#xff08;Greater Sum Tree&#xff09;&#xff0c;使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。 提醒一下&#xff0c;二叉搜索树满足下列约束…

C++11:右值引用、移动语义和完美转发

目录 前言 1. 左值引用和右值引用 2. 引用范围 3. 左值引用的缺陷 4. 右值引用的作用 5. 右值引用的深入场景 6. 完美转发 总结 前言 C11作为一次重大的更新&#xff0c;引入了许多革命性的特性&#xff0c;其中之一便是右值引用和移动语义。本文将深入探讨其中引入的…

Unity抖音直播玩法开发流程

前言 近两年直播玩法逐渐新兴起来了&#xff0c;也出现不少质量还不错的作品&#xff0c;比如下列《红蓝对决》《三国全战》等。近期我们也做了一款直播玩法&#xff0c;就此记录下开发流程。 1&#xff0c;申请应用 进入抖音开发者平台&#xff0c;在首页入驻平台。 如果是…