对于geoserver发布数据后的开发应用
前言
首先,本篇文章仅进行技术分享,某些数据并不会公开。
目前其实还不太会用arcgis发布某些图层数据,但是知道怎么用。
一、geosever管理地理数据的后端实用方法
由于目前工作的局限性,之前的代码都是用geoserver来进行保存的,所以需要构建一个强大的后端处理技术来保证与前端交互的时候,某些服务能服务于非Gis人员。
我的geosever是由docker进行安装的 ,具体可以看下面的文章,用windows直接按照也是可以的,只是我好不容易装好docker不方便我用的话就很累。
docker搭建geoserver
启动下面的镜像
访问本地的8090端口,正常登录即可
查看一下数据预览
接下来要写一下仿照geoserver进行登录的操作,待会我去把这个遥感数据删除,用后端进行管理geoserver的发布。
后端进行登录geoserver并且发布一个矢量数据
因为我要写一个模块,专门用于发布矢量数据并且使用cesium去将这个数据的wms服务给请求下来加载到图层上去。
大致思路是mysql用于持久化系统数据,pgsql对于每一个shp数据进行创建表,用于存贮上传shp数据里的dbf表数据,至于为什么要存这玩意,其实是因为要导出为Excel表方便统计,并且做成功能。
首先是思考的是手动操作的流程,登录geosever,然后得到目标文件的文件路径,发布数据选择样式,发布成功后,获取它的wms服务链接,存到mysql数据库里,得到它的dbf数据,存到pgsql里,下面是这些步骤的代码。
前置的domain数据准备
先写一个实体类先
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import lombok.Data;
import java.util.Date;
/**
* @Author chuxuan
* @Date 2025/5/23
* @Description TODO:这是用于shp数据管理的实体类
*/
@Data
public class ShpDetail {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
private Long id;
/**
* shp名称
*/
@Excel(name = "shp名称")
private String name;
/**
* shp文件名称
*/
@Excel(name = "shp文件名称")
private String fileName;
/**
* geoserver的工作区
*/
@Excel(name = "geoserver的工作区")
private String geoserverWorkArea;
/**
* 数据库表名
*/
@Excel(name = "数据库表名")
private String tableName;
/**
* 数据库字段名,逗号分割
*/
@Excel(name = "数据库字段名,逗号分割")
private String tableField;
/**
* 数据时间
*/
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "数据时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date dataTime;
/**
* 缩略图
*/
@Excel(name = "缩略图")
private String thumbUrl;
/**
* shp坐标编码
*/
@Excel(name = "shp坐标编码")
private String srs;
/**
* wms的url
*/
@Excel(name = "wms的url")
private String wmsUrl;
/**
* 图层名称
*/
@Excel(name = "图层名称")
private String layerName;
/**
* 图层样式名称
*/
@Excel(name = "图层样式名称")
private String sldName;
/**
* 边界
*/
@Excel(name = "边界")
private String box;
/**
* 图例id
*/
@Excel(name = "图例id")
private Long legendId;
/**
* 删除标志(0代表存在 2代表删除)
*/
private String delFlag;
/**
* 中心点纬度
*/
@Excel(name = "中心点纬度")
private String viewCenterLat;
/**
* 中心点经度
*/
@Excel(name = "中心点经度")
private String viewCenterLong;
/**
* 级别,默认11
*/
@Excel(name = "级别,默认11")
private Integer viewZoom;
/**
* 备注
*/
@Excel(name = "备注")
private String remark;
/**
* 创建者
*/
private String createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
private String updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
* 是否包含必要字段
*/
private Boolean needfulStatus;
/**
* 特别排除的字段,有些字段无法保存的需要排除掉
*/
private String excludeFields;
/**
* 图例名曾
*/
@TableField(exist = false)
private String legendName;
}
shp数据添加的AddDTO,作用是在我们上传的时候将发布图层的style等参数都放进去,以及一个简单是shp数据。
由此可以设计DTO
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import java.util.Date;
import java.util.List;
import lombok.Data;
/**
* @Author chuxuan
* @Date 2025/5/23
* @Description TODO:这是用于shp数据上传的DTO类
*/
@Data
public class ShpDetailAddDTO {
private static final long serialVersionUID = 1L;
/** 主键id */
@Excel(name = "主键id")
private Long id;
/** shp文件名称 */
@Excel(name = "shp文件名称")
private String fileName;
/** shp中文简称 */
@Excel(name = "shp中文简称")
private String name;
/** 数据时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "数据时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date dataTime;
/** 数据库表名 */
@Excel(name = "数据库表名")
private String tableName;
/** 图层样式名称 */
@Excel(name = "图层样式名称")
private String sldName;
/** 图例id */
@Excel(name = "图例id")
private Long legendId;
/** geoserver的工作区 */
@Excel(name = "geoserver的工作区")
private String geoserverWorkArea;
/** 图层名称 */
@Excel(name = "图层名称")
private String layerName;
/** 级别,默认11 */
@Excel(name = "级别,默认11")
private Integer viewZoom;
/** 备注 */
@Excel(name = "备注")
private String remark;
/** 中心点纬度 */
@Excel(name = "中心点纬度")
private String viewCenterLat;
/** 中心点经度 */
@Excel(name = "中心点经度")
private String viewCenterLong;
/** 缩略图 */
@Excel(name = "缩略图")
private String thumbUrl;
/** shp坐标编码 */
@Excel(name = "shp坐标编码")
private String srs = "4326";
/** 数据归属区域(可多选) */
@Excel(name = "数据归属区域(可多选)")
private List<Long> areaList;
/** 数据归属分类(可多选) */
@Excel(name = "数据归属分类(可多选)")
private List<Long> classifyList;
@Excel(name = "是否包含必要字段")
private Boolean needfulStatus;
@Excel(name = "特别排除的字段")
private String excludeFields;
}
然后是一个获取geoserver的配置类,需要从数据库中获取,这个类我已经写好了,只需要去写一下方法即可
先指导AI干活,帮我把某个配置类的注解和注释都写正规一点
写好对应的获取geosever的配置
然后因为配置化的原因,写了很久的代码,系统太复杂了,光理解代码都要了很长时间
后端内容
- geoserver的登录
- 数据的上传以及解包
- geoserver的上传参数配置以及相关的数据库配置字段,pgsql的shp数据建表
- geoserver上的以及数据库上的相关增删改查
- 总结来说,3个服务并且使用多个DTO才能组成后端服务
geosever登录
首先是Geosever的Utils工具,写在common里utils里去
import it.geosolutions.geoserver.rest.GeoServerRESTManager;
import it.geosolutions.geoserver.rest.GeoServerRESTPublisher;
import it.geosolutions.geoserver.rest.GeoServerRESTReader;
import it.geosolutions.geoserver.rest.decoder.RESTLayer;
import it.geosolutions.geoserver.rest.encoder.GSLayerEncoder;
import it.geosolutions.geoserver.rest.encoder.datastore.GSShapefileDatastoreEncoder;
import it.geosolutions.geoserver.rest.encoder.feature.GSFeatureTypeEncoder;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Base64;
public class GeoServerUtils {
/**
* shp数据发布
* 图层 发布
*
* @param url
* @param userName
* @param passWord
* @param FilePath 文件路径
* @param workSpace 工作区名称
* @param dataSetName 数据存储名称
* @param layerName 图层名称
*/
public static void publishShape(String url, String userName, String passWord, String FilePath, String workSpace, String dataSetName, String layerName, String type) throws IOException {
File file = new File(FilePath);
// 连接geoServer
GeoServerRESTManager geoServerRESTManager = null;
try {
geoServerRESTManager = new GeoServerRESTManager(new URL(url), userName, passWord);
} catch (Exception e) {
System.out.println("远程连接GeoServer失败...");
e.printStackTrace();
}
// shp读写和发布
assert geoServerRESTManager != null;
GeoServerRESTReader restReader = geoServerRESTManager.getReader();
GeoServerRESTPublisher restPublisher = geoServerRESTManager.getPublisher();
// 存在相应的工作区
if (!restReader.existsWorkspace(workSpace)) {
restPublisher.createWorkspace(workSpace);
} else {
System.out.println("workSpace已经存在了,workSpace :" + workSpace);
}
if (!restReader.existsDatastore(workSpace, dataSetName)) {
//创建shape文件存储
try {
//shp文件所在的位置
String urlDataStorePath = file.getPath();
// 数据存储需要的文件
String shpFilePath = String.format("file://%s", urlDataStorePath);
URL urlShapeFile = new URL(shpFilePath);
// 创建数据集
GSShapefileDatastoreEncoder datastoreEncoder = new GSShapefileDatastoreEncoder(dataSetName, urlShapeFile);
datastoreEncoder.setCharset(Charset.forName("UTF-8"));
geoServerRESTManager.getStoreManager().create(workSpace, datastoreEncoder);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
// 图层layer
if (!restReader.existsLayer(workSpace, layerName)) {
try {
GSFeatureTypeEncoder gsFeatureTypeEncoder = new GSFeatureTypeEncoder();
gsFeatureTypeEncoder.setTitle(layerName);
gsFeatureTypeEncoder.setName(layerName);
gsFeatureTypeEncoder.setSRS(GeoServerRESTPublisher.DEFAULT_CRS);
GSLayerEncoder gsLayerEncoder = new GSLayerEncoder();
// gsLayerEncoder.addStyle("grass");
boolean layer = restPublisher.publishDBLayer(workSpace, dataSetName, gsFeatureTypeEncoder, gsLayerEncoder);
System.out.println("publish layer : " + layer);
} catch (Exception e) {
e.printStackTrace();
}
}
//绑定样式sld
if (!StringUtils.isEmpty(type)){
styleBinding(url,workSpace,layerName,type,userName,passWord);
}
// styleBinding(url,workSpace,layerName,type+"_style",userName,passWord);
}
public static String GeoServerReader(String url, String userName, String passWord, String workspaceName, String workspaceLayerName) throws IOException {
GeoServerRESTReader restReader = new GeoServerRESTReader(url, userName, passWord);
OkHttpClient client = new OkHttpClient();
String bbox = null;
try {
// 构建请求
Request request = new Request.Builder()
.url(url + "/wms?request=GetCapabilities&service=WMS&version=1.3.0")
.build();
// 发送请求并获取响应
Response response = client.newCall(request).execute();
String responseBody = response.body().string();
// 解析 GetCapabilities 响应
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(responseBody)));
// 查找指定图层的边界框信息
NodeList layerNodes = doc.getElementsByTagName("Layer");
for (int i = 0; i < layerNodes.getLength(); i++) {
Element layerElement = (Element) layerNodes.item(i);
//旧逻辑
String layerWorkspace = layerElement.getAttribute("queryable");
String layerTitle = layerElement.getElementsByTagName("Name").item(0).getTextContent();
//新逻辑
// String layerWorkspace = workspaceName;
// String layerTitle = workspaceLayerName;
// if (layerWorkspace.equals(workspaceName) || layerTitle.equals(workspaceLayerName)) {
bbox = layerElement.getElementsByTagName("EX_GeographicBoundingBox").item(0).getTextContent();
break;
// }
}
// 关闭响应
response.close();
} catch (IOException | ParserConfigurationException | SAXException e) {
e.printStackTrace();
}
String formatting = GeoServerUtils.formatting(bbox);
return formatting;
}
/**
* 数据格式处理方法
*
* @param data
* @return
*/
public static String formatting(String data) {
data = data.replaceAll("\\s+(?=\\d)", ",");
data = String.join(",", data.replace("\n", ","));
// 去除逗号后的空格
data = data.replaceAll(",\\s+", ",");
data = data.replaceFirst(",", "");
data = data.replaceAll(",$", "");
String[] parts = data.split(",");
String output = null;
if (parts.length >= 4) {
// 交换第一个逗号后面的数和第二个逗号后面的数
String temp = parts[1];
parts[1] = parts[2];
parts[2] = temp;
// 重新组合成一个新的字符串
output = String.join(",", parts);
}
return output;
}
/**
* 图层样式绑定
*
* @param geoServerUrl
* @param workspace
* @param layerName
* @param styleName
* @param username
* @param password
* @throws IOException
*/
public static void styleBinding(String geoServerUrl, String workspace, String layerName, String styleName, String username, String password) throws IOException {
// 构建REST API请求URL
String urlStr = geoServerUrl + "rest/layers/" + workspace + ":" + layerName;
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 设置HTTP方法为PUT
conn.setRequestMethod("PUT");
conn.setDoOutput(true);
// 设置基本认证
String auth = username + ":" + password;
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
conn.setRequestProperty("Authorization", "Basic " + encodedAuth);
// 设置Content-Type为application/xml
conn.setRequestProperty("Content-Type", "application/xml");
// 构建XML数据
String xmlData = "<layer>" +
"<defaultStyle>" +
"<name>" + styleName + "</name>" +
"</defaultStyle>" +
"</layer>";
// 发送请求数据
OutputStream os = conn.getOutputStream();
os.write(xmlData.getBytes());
os.flush();
// 获取响应码
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
System.out.println("样式绑定成功!");
} else {
System.out.println("样式绑定失败,响应码:" + responseCode);
}
}
public static void deleteLayer(String geoServerUrl, String username, String password, String workspace, String layerName) throws MalformedURLException {
// 创建 GeoServer REST 管理器
URL url = new URL(geoServerUrl);
GeoServerRESTManager manager = new GeoServerRESTManager(url, username, password);
GeoServerRESTPublisher publisher = manager.getPublisher();
// 删除图层
boolean deleted = publisher.unpublishFeatureType(workspace, layerName, layerName);
//删除数据集
boolean deletedData = publisher.removeDatastore(workspace,layerName,true);
if (deleted && deletedData) {
System.out.println("Layer '" + layerName + "' deleted successfully.");
} else {
System.out.println("Failed to delete layer '" + layerName + "'.");
}
}
}
然后通过引用这些数据就能实现连接geoserver并进行管理增删改查
String url = hsSysConfigService.getGeoserverUrl();//获取url配置
String username = hsSysConfigService.getGeoserverUsername();//获取geoserver的用户名
String password = hsSysConfigService.getGeoserverPassword();//获取geoserver的密码
String uploadDir = hsSysConfigService.getUploadUrl()+"/"+dto.getFileName()+"/"; // 文件保存的路径
String filePath = file.getPath();
File file = new File(uploadDir +dto.getFileName()+".shp");
// 如果存在".",则截取文件名的子串
String filenameWithoutExtension = dto.getFileName();
try {
//这个是一个上传到geoserver的例子
//1.模拟首先是登录
//2.然后文件路径必须是shp矢量数据的路径,
//然后dto的getGeoserverWorkArea是工作空间,
//filenameWithoutExtension是文件前缀
//最后hsLegend.getSldStyle()是geoserver里的样式
GeoServerUtils.publishShape(url, username, password, filePath, dto.getGeoserverWorkArea(), filenameWithoutExtension, filenameWithoutExtension,hsLegend.getSldStyle());
} catch (IOException e) {
e.printStackTrace();
}
上传就不多写了,其实如果说,上传的数据是需要合并的数据的话,那就上传一个压缩包,最大大小自己注意即可
读取矢量数据的工具库
首先是建立一些有用的,要做到的有合并矢量数据以及读取矢量数据
import java.util.*;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureBuilder;
/**
* 矢量数据合并
*
* @param list 矢量文件集合
* @param outputFile 文件输出路径
*/
public static void mergeShapeFiles(List<String> list, String outputFile) throws IOException {
List<SimpleFeatureCollection> featureCollections = new ArrayList<>();
// 读取所有输入的Shapefile文件
for (String filePath : list) {
File file = new File(filePath);
ShapefileDataStore dataStore = new ShapefileDataStore(file.toURI().toURL());
SimpleFeatureSource featureSource = dataStore.getFeatureSource();
featureCollections.add(featureSource.getFeatures());
dataStore.dispose();
}
// 获取Shapefile的Feature类型
SimpleFeatureType schema = featureCollections.get(0).getSchema();
// 使用DefaultFeatureCollection来存储合并后的数据
DefaultFeatureCollection mergedCollection = new DefaultFeatureCollection(null, schema);
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(schema);
// 合并所有FeatureCollection中的数据
for (SimpleFeatureCollection featureCollection : featureCollections) {
try (SimpleFeatureIterator iterator = featureCollection.features()) {
while (iterator.hasNext()) {
SimpleFeature feature = iterator.next();
featureBuilder.addAll(feature.getAttributes());
mergedCollection.add(featureBuilder.buildFeature(null));
}
}
}
File dir = new File(outputFile);
if (!dir.exists()) {
dir.mkdirs();
}
outputFile = outputFile + "/result.shp";
// 准备ShapefileDataStoreFactory所需的参数
File newFile = new File(outputFile);
Map<String, Serializable> params = new HashMap<>();
params.put("url", newFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE); // 可选:创建空间索引
// 创建新的Shapefile数据存储
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
newDataStore.createSchema(schema);
Transaction transaction = new DefaultTransaction("create");
SimpleFeatureSource featureSource = newDataStore.getFeatureSource(newDataStore.getTypeNames()[0]);
if (featureSource instanceof SimpleFeatureStore) {
SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
featureStore.setTransaction(transaction);
featureStore.addFeatures(mergedCollection);
transaction.commit();
transaction.close();
} else {
System.out.println("无法写入: " + outputFile);
}
newDataStore.dispose();
}
}
读取矢量信息中的属性
public static List<Map<String, Object>> shpDataInfo(File file) throws IOException {
List<Map<String, Object>> featuresList = new ArrayList<>();
// 创建FileDataStore并获取FeatureSource
ShapefileDataStore store = (ShapefileDataStore) FileDataStoreFinder.getDataStore(file);
store.setCharset(Charset.forName("UTF-8")); // 设置字符编码
SimpleFeatureSource featureSource = store.getFeatureSource();
store.dispose();
// 获取Shapefile的FeatureType
SimpleFeatureType schema = featureSource.getSchema();
// 遍历每个要素并读取其属性
try (FeatureIterator<SimpleFeature> features = featureSource.getFeatures().features()) {
while (features.hasNext()) {
HashMap map = new HashMap();
SimpleFeature feature = features.next();
for (AttributeDescriptor attr : schema.getAttributeDescriptors()) {
String attrName = attr.getName().toString();
Object attrValue = feature.getAttribute(attrName);
map.put(attrName, attrValue);
}
featuresList.add(map);
}
return featuresList;
}
}
对应需要使用的maven库
<!--GeoServer-->
<dependency>
<groupId>it.geosolutions</groupId>
<artifactId>geoserver-manager</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>23-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-main</artifactId>
<version>23-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-referencing</artifactId>
<version>23-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.25</version>
</dependency>
至此很多路线就打通了
还没写Pgsql管理里面的shp数据,以后再写了
总结
后面会出一下cesium加载模型并变色的教程的。