【java】【geoserver】java实现对geoserver图层及数据库的数据增删改

geotools请参考:geotools实现插入,更新,查询

包结构

在这里插入图片描述
采用技术:

dom4j,ThreadPoolTaskExecutor线程池,RestTemplate,guava retry,wfs协议,ogc协议,btoa加密,策略模式

通过geoserver来修改postgresql地理数据库,这样就不需要每次更新数据后,人为手动更新geoserver了

相关配置

相关maven

这里只贴了关键的maven

<repositories>
	<repository>
		<id>osgeo</id>
		<name>osgeo Repository</name>
		<url>https://repo.osgeo.org/repository/release/</url>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
	</repository>

	<repository>
		<id>aliyun</id>
		<name>aliyun Repository</name>
		<url>https://maven.aliyun.com/repository/public</url>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
	</repository>
</repositories>

<dependencies>
	<dependency>
		<groupId>org.geotools</groupId>
		<artifactId>gt-main</artifactId>
		<version>25.2</version>
	</dependency>

	<!-- https://mvnrepository.com/artifact/org.geotools/gt-shapefile -->
	<dependency>
		<groupId>org.geotools</groupId>
		<artifactId>gt-shapefile</artifactId>
		<version>25.2</version>
	</dependency>

	<!-- https://mvnrepository.com/artifact/org.geotools.jdbc/gt-jdbc-postgis -->
	<dependency>
		<groupId>org.geotools.jdbc</groupId>
		<artifactId>gt-jdbc-postgis</artifactId>
		<version>25.2</version>
	</dependency>

	<dependency>
		<groupId>org.geotools</groupId>
		<artifactId>gt-epsg-hsql</artifactId>
		<version>25.2</version>
	</dependency>

	<dependency>
		<groupId>it.geosolutions</groupId>
		<artifactId>geoserver-manager</artifactId>
		<version>1.7.0</version>
	</dependency>
	<!--重试机制-->
	<dependency>
		<groupId>com.github.rholder</groupId>
		<artifactId>guava-retrying</artifactId>
		<version>2.0.0</version>
	</dependency>
	<!-- json -->
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>fastjson</artifactId>
		<version>1.2.75</version>
	</dependency>
	<!-- Lombok -->
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
	</dependency>
	<!-- dom4j -->
	<dependency>
		<groupId>dom4j</groupId>
		<artifactId>dom4j</artifactId>
		<version>1.6.1</version>
	</dependency>
</dependencies>

retry

import com.github.rholder.retry.Retryer;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.TimeUnit;

/**
 * @author qinlei
 * @description todo
 * @date 2021/10/26 9:55
 */
@Configuration
public class RetryConfig {

    @Bean
    public Retryer<Boolean> instance(){
        Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
                //返回false也需要重试
                .retryIfResult(Predicates.equalTo(false))
                //抛出runtime异常,checked异常时都会重试,但是抛出error不会重试
                .retryIfException()
                //重试策略
                .withWaitStrategy(WaitStrategies.fixedWait(10, TimeUnit.SECONDS))
                //尝试次数
                .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                .build();
        return retryer;
    }
}

restTemplate

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
* 优雅的http请求方式RestTemplate
* @Return:
*/
@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        return new RestTemplate(factory);
    }

    @Bean
    public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(5000);//ms
        factory.setConnectTimeout(15000);//ms
        return factory;
    }
}

ThreadPoolExecutor配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author qinlei
 * @description 线程池配置
 * @date 2021/4/14 11:35
 */
@Configuration
public class ThreadsPoolsConfig {
    @Bean
    public void initThreadPool(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(1);
        //配置最大线程数
        executor.setMaxPoolSize(20);
        //配置队列大小
        executor.setQueueCapacity(100);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("async-service-");
        //线程空闲后的最大存活时间
        executor.setKeepAliveSeconds(10);
        executor.setAllowCoreThreadTimeOut(true);
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
    }
}

util

btoa加密

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author qinlei
 * @description todo
 * @date 2021/10/21 16:03
 */
public class BtoaEncode {
    //btoa加密方法
    public static String base64hash = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


    /*
            str:要匹配的数据
            reg:正则匹配的规则
         */
    public static boolean isMatcher(String str,String reg){
        //编译成一个正则表达式模式
        Pattern pattern = Pattern.compile(reg);
        //匹配模式
        Matcher matcher = pattern.matcher(str);
        if(matcher.matches()){
            return true;
        }
        return false;
    }

    public static String botaEncodePassword(String pwd){
        if(pwd==null||isMatcher(pwd,"([^\\u0000-\\u00ff])")){
            throw new Error("INVALID_CHARACTER_ERR");
        }
        int i = 0,prev=0,ascii,mod=0;
        StringBuilder result = new StringBuilder();
        while (i<pwd.length()){
            ascii = pwd.charAt(i);
            mod = i%3;
            switch (mod){
                //第一个6位只需要让8位二进制右移两位
                case 0:
                    result.append(base64hash.charAt(ascii>>2));
                    break;
                //第二个6位=第一个8位的后两位+第二个八位的前四位
                case 1:
                    result.append(base64hash.charAt((prev&3)<<4|(ascii>>4)));
                    break;
                //第三个6位=第二个8位的后四位+第三个8位的前两位
                //第四个6位 = 第三个8位的后6位
                case 2:
                    result.append(base64hash.charAt((prev & 0x0f)<<2|(ascii>>6)));
                    result.append(base64hash.charAt(ascii&0x3f));
                    break;
            }
            prev = ascii;
            i++;
        }
        //循环结束后看mod, 为0 证明需补3个6位,第一个为最后一个8位的最后两位后面补4个0。另外两个6位对应的是异常的“=”;
        // mod为1,证明还需补两个6位,一个是最后一个8位的后4位补两个0,另一个对应异常的“=”
        if(mod == 0){
            result.append(base64hash.charAt((prev&3)<<4));
            result.append("==");
        }else if(mod == 1){
            result.append(base64hash.charAt((prev&0x0f)<<2));
            result.append("=");
        }

        return result.toString();

    }
}

xml转换相关工具类

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import java.util.List;

/**
 * @author qinlei
 * @description todo
 * @date 2021/10/21 23:21
 */
public class XMLUtils {
    /**
     * 将xml转换为JSON对象
     *
     * @param xml
     *            xml字符串
     * @return
     * @throws Exception
     */
    public static JSONObject xmltoJson(String xml) throws Exception {
        JSONObject jsonObject = new JSONObject();
        Document document = DocumentHelper.parseText(xml);
        // 获取根节点元素对象
        Element root = document.getRootElement();
        iterateNodes(root, jsonObject);
        return jsonObject;
    }

    /**
     * 遍历元素
     *
     * @param node
     *            元素
     * @param json
     *            将元素遍历完成之后放的JSON对象
     */
    @SuppressWarnings("unchecked")
    public static void iterateNodes(Element node, JSONObject json) {
        // 获取当前元素的名称
        String nodeName = node.getName();
        // 判断已遍历的JSON中是否已经有了该元素的名称
        if (json.containsKey(nodeName)) {
            // 该元素在同级下有多个
            Object Object = json.get(nodeName);
            JSONArray array = null;
            if (Object instanceof JSONArray) {
                array = (JSONArray) Object;
            } else {
                array = new JSONArray();
                array.add(Object);
            }
            // 获取该元素下所有子元素
            List<Element> listElement = node.elements();
            if (listElement.isEmpty()) {
                // 该元素无子元素,获取元素的值
                String nodeValue = node.getTextTrim();
                array.add(nodeValue);
                json.put(nodeName, array);
                return;
            }
            // 有子元素
            JSONObject newJson = new JSONObject();
            // 遍历所有子元素
            for (Element e : listElement) {
                // 递归
                iterateNodes(e, newJson);
            }
            array.add(newJson);
            json.put(nodeName, array);
            return;
        }
        // 该元素同级下第一次遍历
        // 获取该元素下所有子元素
        List<Element> listElement = node.elements();
        if (listElement.isEmpty()) {
            // 该元素无子元素,获取元素的值
            String nodeValue = node.getTextTrim();
            json.put(nodeName, nodeValue);
            return;
        }
        // 有子节点,新建一个JSONObject来存储该节点下子节点的值
        JSONObject object = new JSONObject();
        // 遍历所有一级子节点
        for (Element e : listElement) {
            // 递归
            iterateNodes(e, object);
        }
        json.put(nodeName, object);
        return;
    }
}

wfs-t 核心操作

WFSTXmlGenerate

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.jeecg.modules.wfs.exception.GeoXmlQlException;

import java.util.List;
import java.util.Map;

/**
 * @author qinlei
 * @description todo
 * @date 2021/10/21 16:44
 */
public abstract class WFSTXmlGenerate {

    public static final String INSERT_METHOD = "Insert";
    public static final String UPDATE_METHOD = "Update";
    public static final String DELETE_METHOD = "Delete";

    private Document document;

    public Document getDocument() {
        return document;
    }

    public WFSTXmlGenerate() {
        document = DocumentHelper.createDocument();
        Element wfs = document.addElement("wfs:Transaction");
        wfs.addAttribute("version", "1.0.0");
        wfs.addAttribute("service", "WFS");
        wfs.addNamespace("ogc", "http://www.opengis.net/ogc");
        wfs.addNamespace("wfs", "http://www.opengis.net/wfs");
        wfs.addNamespace("gml", "http://www.opengis.net/gml");
        wfs.addNamespace("xsi", "http://www.opengis.net/xsi");
        wfs.addAttribute("xsi:schemaLocation", "http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd");
    }

    /**
     * 生成xml方法
     */
    public abstract Document createXml(String businessId,String username, String typeName, String wfsPath, Map<String, Object> variables, Map<String, Object> params)throws GeoXmlQlException;

    /**
     * 创建k-v的节点
     *
     * @param property
     * @param k
     * @param v geom时,该v为jsonObject
     * @param workspace 工作空间(geom需要 其余设置为空)
     */
    void createKVNode(Element property, String k, Object v, String workspace) throws GeoXmlQlException {
        if (StringUtils.isBlank(workspace)) {
            Element key = property.addElement("wfs:Name");
            key.setText(k);
            Element value = property.addElement("wfs:Value");
            if (v != null)
                value.setText(v.toString());
            return;
        }
        Element element = property.addElement(workspace + ":" + k);
        if(v==null)
            return;
        JSONObject valueJson = JSON.parseObject(JSONObject.toJSONString(v));
        String type = valueJson.getString("type");
        if("general".equals(type) && v!=null){
            element.setText(valueJson.getString("value"));
        }else if("MultiLineString".equals(type)){
            String coordinates = valueJson.getString("coordinates");
            this.createMultiLineEle(element,type,coordinates);
        }else if("Point".equals(type)){
            String coordinates = valueJson.getString("coordinates");
            this.createPointEle(element,type,coordinates);
        }else{

        }

    }

    private void createMultiLineEle(Element element,String type,String coordinates){
        Element multiLineString = createGeomEle(element,type);
        Element lineStringMember = multiLineString.addElement("gml:lineStringMember");
        Element LineString = lineStringMember.addElement("gml:LineString");
        createCoorEle(LineString,coordinates);
    }

    private void createPointEle(Element element,String type,String coordinates){
        Element point = createGeomEle(element,type);
        createCoorEle(point,coordinates);
    }

    private Element createGeomEle(Element element, String type){
        Element point = element.addElement("gml:" + type);
        point.addAttribute("srsName", "http://www.opengis.net/gml/srs/epsg.xml#4326");
        return point;
    }

    private void createCoorEle(Element toPoint,String coordinates){
        Element coor = toPoint.addElement("gml:coordinates");
        coor.addAttribute("decimal", ".");
        coor.addAttribute("cs", ",");
        coor.addAttribute("ts", ";");
        coor.setText(coordinates);
    }


    public void createUpdateNode(Element element, String typeName, Map<String,Object> variables, Map<String,Object> params) throws GeoXmlQlException {
        Element updateNode = element.addElement("wfs:"+UPDATE_METHOD);
        updateNode.addAttribute("typeName",typeName);
        for (Map.Entry<String, Object> entry : variables.entrySet()) {
            Element property = updateNode.addElement("wfs:Property");
            this.createKVNode(property,entry.getKey(),entry.getValue(),null);
        }
        createFilterNode(updateNode,typeName.split(":")[0],params);
    }

    public void createInsertNode(String wfsPath,Element element, String typeName, Map<String,Object> variables) throws GeoXmlQlException {
        String[] typeSplit = typeName.split(":");
        String workspace = typeSplit[0];
        element.addNamespace(workspace,"http://"+workspace);
        element.addAttribute("xsi:schemaLocation",element.attributeValue("xsi:schemaLocation")+" http://"+workspace+" "
                + wfsPath+"/DescribeFeatureType?typename="+typeName);
        Element insertNode = element.addElement("wfs:"+INSERT_METHOD);
        Element property = insertNode.addElement(typeName);
        for (Map.Entry<String, Object> entry : variables.entrySet()) {
            this.createKVNode(property,entry.getKey(),entry.getValue(),workspace);
        }
//        createFilterNode(element,workspace,params);
    }

    public void createDeleteNode(Element element, String typeName, Map<String, Object> variables, Map<String, Object> params) throws GeoXmlQlException {
        Element deleteNode = element.addElement("wfs:" + DELETE_METHOD);
        deleteNode.addAttribute("typeName", typeName);
        createFilterNode(deleteNode,typeName.split(":")[0], params);
    }

    /**
     * 创建fid过滤器节点
     * 为了安全性考虑 fids不允许非空 目前只提供id的过滤
     *
     * @param element
     * @param params
     */
    void createFilterNode(Element element,String workspace, Map<String, Object> params) throws GeoXmlQlException {
        if (params == null || params.isEmpty()) {
            return;
        }
        Element filter = element.addElement("ogc:Filter");
        for (Map.Entry<String, Object> objectEntry : params.entrySet()) {
            Element property = filter.addElement("ogc:PropertyIsEqualTo");
            Element nameEle = property.addElement("ogc:PropertyName");
            nameEle.setText(workspace+":"+objectEntry.getKey());
            Element literalEle = property.addElement("ogc:Literal");
            literalEle.setText(objectEntry.getValue() == null ? null : objectEntry.getValue().toString());
        }
    }

}

插入操作实现类

import org.dom4j.Document;
import java.util.Map;

/**
 * @author qinlei
 * @description todo
 * @date 2021/10/21 18:05
 */
public class InsertWfsXmlGenerate extends WFSTXmlGenerate{

    public InsertWfsXmlGenerate() {
        super();
    }

    @Override
    public Document createXml(String businessId,String updateUser,String typeName,String wfsPath, Map<String, Object> variables, Map<String, Object> params) {
        createInsertNode(wfsPath,this.getDocument().getRootElement(),typeName,variables);
        return this.getDocument();
    }

}

修改编辑操作实现类

import org.dom4j.Document;
import org.jeecg.modules.wfs.exception.GeoXmlQlException;

import java.util.Map;

/**
 * @author qinlei
 * @description todo
 * @date 2021/10/21 18:05
 */
public class UpdateWfsXmlGenerate extends WFSTXmlGenerate {
    public UpdateWfsXmlGenerate() {
        super();
    }

    @Override
    public Document createXml(String businessId,String updateUser,String typeName,String wfsPath, Map<String, Object> variables, Map<String, Object> params) throws GeoXmlQlException{
        this.createUpdateNode(this.getDocument().getRootElement(),typeName,variables,params);
        return this.getDocument();
    }

}

删除操作实现类

import org.dom4j.Document;
import java.util.Map;

/**
 * @author qinlei
 * @description todo
 * @date 2021/10/21 18:05
 */
class DeleteWfsXmlGenerate extends WFSTXmlGenerate {
    public DeleteWfsXmlGenerate() {
        super();
    }

    @Override
    public Document createXml(String businessId, String updateUser, String typeName, String wfsPath, Map<String, Object> variables, Map<String, Object> params) {
        this.createDeleteNode(this.getDocument().getRootElement(), typeName, variables, params);
        return this.getDocument();
    }
}

插入加修改一个事务操作实现类

import org.dom4j.Document;
import org.jeecg.common.util.DateUtils;
import java.util.HashMap;
import java.util.Map;

/**
 * @author qinlei
 * @description todo
 * @date 2021/10/25 11:33
 */
public class InsertUpdateWfsXmlGenerate extends WFSTXmlGenerate{

    public InsertUpdateWfsXmlGenerate() {
        super();
    }

    @Override
    public Document createXml(String businessId,String username,String typeName,String wfsPath, Map<String, Object> variables, Map<String, Object> params) {
        Map<String, Object> vars = new HashMap<>();
        vars.put("isvalid","0");
        vars.put("uploader",username);
        vars.put("uploadtime",DateUtils.getTimestamp());
        createUpdateNode(this.getDocument().getRootElement(),typeName,vars,params);
        createGeneralParams(variables,"isvalid","1");
        createGeneralParams(variables,"surveyor",username);
        createGeneralParams(variables,"surveydate", DateUtils.formatDate());
        createGeneralParams(variables,"index_id",businessId);
        createInsertNode(wfsPath,this.getDocument().getRootElement(),typeName,variables);
        return this.getDocument();
    }

    private void createGeneralParams(Map<String, Object> variables,String key,Object param){
        Map<String, Object> map = new HashMap<>();
        map.put("type","general");
        map.put("value",param);
        variables.put(key,map);
    }
}

异常类

/**
 * @author qinlei
 * @description geoserver专用异常类
 * @date 2021/10/21 17:56
 */
public class GeoXmlQlException extends RuntimeException{
    private static final long serialVersionUID = 1L;

    public GeoXmlQlException(String message){
        super(message);
    }

    public GeoXmlQlException(Throwable cause)
    {
        super(cause);
    }

    public GeoXmlQlException(String message, Throwable cause)
    {
        super(message,cause);
    }
}

GEOTools封装工具

import com.alibaba.fastjson.JSONObject;
import com.github.rholder.retry.RetryException;
import com.github.rholder.retry.Retryer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.geotools.data.*;
import org.geotools.data.postgis.PostgisNGDataStoreFactory;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.feature.AttributeTypeBuilder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.referencing.CRS;
import org.jeecg.common.util.RestUtil;
import org.jeecg.modules.wfs.exception.GeoXmlQlException;
import org.jeecg.modules.wfs.xmlGenerate.service.InsertUpdateWfsXmlGenerate;
import org.jeecg.modules.wfs.xmlGenerate.util.BtoaEncode;
import org.jeecg.modules.wfs.xmlGenerate.service.WFSTXmlGenerate;
import org.jeecg.modules.wfs.xmlGenerate.util.XMLUtils;
import org.opengis.feature.Feature;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.ExecutionException;

@Slf4j
@Service
public class GeoToolsManager {

    public static final int CREATE = 0;
    public static final int APPEND = 1;
    public static final int OVERWRITE = 2;

    private static CoordinateReferenceSystem crs4490;

    @Autowired
    private Retryer<Boolean> retryer;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    public GeoToolsManager() {
        try {
            crs4490 = CRS.decode("EPSG:4490");
        } catch (FactoryException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据数据库连接参数,获取数据源
     *
     * @return
     * @throws IOException
     */
    private DataStore getPostgisDataStore(String host, String port, String user, String password, String database, String schema) throws IOException {
        Map<String, Object> params = new HashMap<>();
        params.put("dbtype", "postgis");
        params.put("host", host);
        params.put("port", port);
        params.put("schema", schema);
        params.put("database", database);
        params.put("user", user);
        params.put("passwd", password);
        params.put(PostgisNGDataStoreFactory.LOOSEBBOX.key, true);
        /**
         * 开启下面后测试数据(7000+要素)由原来的19秒加快到13秒
         */
        params.put(PostgisNGDataStoreFactory.PREPARED_STATEMENTS.key, true);
        System.out.println("连接数据库");
        return DataStoreFinder.getDataStore(params);
    }

    public boolean shp2postgis(String shppath, String host, String port, String user, String password, String database, String schema, String tablename, String encode, int option) {
        // 首先尝试连接数据库
        DataStore pgDatastore = null;
        try {
            pgDatastore = getPostgisDataStore(host, port, user, password, database, schema);
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("连接数据库失败");
            return false;
        }

        // 尝试读取shp
        ShapefileDataStore shpDataStore = null;
        try {
            shpDataStore = createShpDataStore(shppath, encode);
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("读取shp失败");
            return false;
        } catch (FactoryException e) {
            e.printStackTrace();
            System.err.println("读取shp失败");
            return false;
        }

        // 写入数据
        return writePostGIS(pgDatastore, shpDataStore, tablename, option);
    }

    /**
     * 根据shp文件创建数据源
     *
     * @param shppath
     * @param encode
     * @return
     * @throws IOException
     * @throws FactoryException
     */
    private ShapefileDataStore createShpDataStore(String shppath, String encode) throws IOException, FactoryException {
        ShapefileDataStore shapefileDataStore = new ShapefileDataStore(new File(shppath).toURI().toURL());
        // 根据用户指定的字符集编码设置解析编码
        shapefileDataStore.setCharset(Charset.forName(encode));
        // 如果没有指定坐标系,则强制使用4490
        CoordinateReferenceSystem crs = shapefileDataStore.getSchema().getCoordinateReferenceSystem();
        if (null == crs) {
            crs = crs4490;
            shapefileDataStore.forceSchemaCRS(crs);
        }
        // 根据数据源获取数据类型
        return shapefileDataStore;
    }

    /**
     * 根据数据源获取要素集合的迭代器
     *
     * @param dataStore
     * @return
     * @throws IOException
     */
    private FeatureIterator getIterator(ShapefileDataStore dataStore) throws IOException {
        // 获取shp数据源
        FeatureSource featureSource = dataStore.getFeatureSource();
        FeatureCollection featureCollection = featureSource.getFeatures();
        return featureCollection.features();
    }

    /**
     * 根据一个shp要素类型创建一个新的要素类型
     *
     * @param tablename
     * @param shpfeaturetype
     * @return
     */
    private SimpleFeatureType createPostGISType(String tablename, SimpleFeatureType shpfeaturetype) {
        SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
        // 这些需要设置下默认空间字段,否则不会识别
        typeBuilder.setDefaultGeometry("geom");
        // 这里设置一下名称,当shp文件名是大写时需要换成小写
        typeBuilder.setName(tablename);
        // 设置坐标系,单独设置没有起到作用
        CoordinateReferenceSystem crs = shpfeaturetype.getCoordinateReferenceSystem();
        typeBuilder.setCRS(crs);
        // 新创建一个属性描述集合
        List<AttributeDescriptor> attributeDescriptors = new ArrayList<>();
        for (AttributeDescriptor attributeDescriptor : shpfeaturetype.getAttributeDescriptors()) {
            //属性构造器
            AttributeTypeBuilder build = new AttributeTypeBuilder();
            build.init(attributeDescriptor.getType());
            build.setNillable(true);
            //获取字段名,改为小写
            String name = StringUtils.isNotEmpty(attributeDescriptor.getLocalName()) ? attributeDescriptor.getLocalName().toLowerCase() : attributeDescriptor.getLocalName();
            if (attributeDescriptor instanceof GeometryDescriptor) {
                //修改空间字段名
                name = "geom";
            }
            //设置字段名
            build.setName(name);
            //创建新的属性类
            AttributeDescriptor descriptor = build.buildDescriptor(name, attributeDescriptor.getType());
            attributeDescriptors.add(descriptor);
        }
        typeBuilder.addAll(attributeDescriptors);
        return typeBuilder.buildFeatureType();
    }

    /**
     * 将shp数据源写入postgis数据源
     *
     * @param pgDatastore
     * @param shpDataStore
     * @param tablename
     * @param option
     * @return
     * @throws IOException
     */
    private boolean writePostGIS(DataStore pgDatastore, ShapefileDataStore shpDataStore, String tablename, int option) {
        SimpleFeatureType shpfeaturetype = null;
        try {
            shpfeaturetype = shpDataStore.getSchema();
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("shp读取失败");
            return false;
        }
        // 原shp数据源的迭代器
        FeatureIterator iterator = null;
        try {
            iterator = getIterator(shpDataStore);
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("shp读取失败");
            return false;
        }

        // 新建一个postgis要素类型构造器
        SimpleFeatureType postGISType = this.createPostGISType(tablename, shpfeaturetype);
        // 新建一个事务
        Transaction transaction = new DefaultTransaction();
        // 新建一个要素写对象
        FeatureWriter<SimpleFeatureType, SimpleFeature> featureWriter = null;
        try {
            // 表操作
            switch (option) {
                case CREATE:
                    pgDatastore.createSchema(postGISType);
                    System.out.println("postgis创建数据表成功");
                    break;
                case APPEND:
                    break;
                case OVERWRITE:
                    pgDatastore.removeSchema(tablename);
                    System.out.println("postgis删除数据表成功");
                    pgDatastore.createSchema(postGISType);
                    System.out.println("postgis创建数据表成功");
                    break;
                default:
                    System.err.println("非法操作");
                    return false;
            }
            // 要素操作
            featureWriter = pgDatastore.getFeatureWriterAppend(tablename, transaction);
            System.out.println("开始写入");
            Date startTime = new Date();
            // 定义循环用到的变量
            Feature feature = null;
            SimpleFeature simpleFeature = null;
            Collection<Property> properties = null;
            Property property = null;
            String pname = null;
            Object pvalue = null;
            while (iterator.hasNext()) {
                feature = iterator.next();
                simpleFeature = featureWriter.next();
                properties = feature.getProperties();
                Iterator<Property> propertyIterator = properties.iterator();
                while (propertyIterator.hasNext()) {
                    property = propertyIterator.next();
                    pname = property.getName().toString().toLowerCase();
                    if ("the_geom".equals(pname)) {
                        pname = "geom";
                    }
                    // 如果值是空字符串则换成Null,表示无值
                    pvalue = property.getValue();
                    if (null == pvalue || (null != pvalue && StringUtils.isEmpty(property.getValue().toString()))) {
                        pvalue = null;
                    }
                    simpleFeature.setAttribute(pname, pvalue);
                }
                featureWriter.write();
            }
            // 最后提交事务,可以加快导入速度
            transaction.commit();
            Date endTime = new Date();
            System.out.println("写入结束,共耗时:" + (endTime.getTime() - startTime.getTime()));
            System.out.println("shp导入postgis成功");
            return true;
        } catch (IOException e) {
            try {
                transaction.rollback();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
            return false;
        } finally {
            try {
                transaction.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (null != iterator) {
                iterator.close();
            }
            if (null != featureWriter) {
                try {
                    featureWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            shpDataStore.dispose();
            pgDatastore.dispose();
        }
    }


    private HttpHeaders createAuthorHeader(String username, String password) {
        HttpHeaders headers = RestUtil.getHeaderApplicationJson();
        headers.add("Authorization", "Basic " + BtoaEncode.botaEncodePassword(username + ":" + password));
        return headers;
    }

    @Value("${geoserver.wfs.error.path}")
    private String path;

    private String xmlString = null;
    /**
     *
     * @param path 输出的地址
     * @param content 写入的内容
     */
    private void writeFile2Disk(String path,String content){
        File file = new File(path);
        if (!file.getParentFile().exists()) {
            boolean result = file.getParentFile().mkdirs();
        }
        FileOutputStream fos= null;
        OutputStreamWriter osw= null;
        BufferedWriter  bw= null;
        try {
            fos = new FileOutputStream(file);
            osw = new OutputStreamWriter(fos, "UTF-8");
            bw = new BufferedWriter(osw);
            bw.write(content);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("=====> 写入error xml 失败",e);
        } finally {
            //注意关闭的先后顺序,先打开的后关闭,后打开的先关闭
            try {
                if(bw!=null){
                    bw.close();
                }
                if(osw!=null){
                    osw.close();
                }
                if(fos!=null){
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                log.error("file流关闭失败,",e);
            }
        }


    }

    /**
     *
     * @param ip
     * @param port
     * @param username
     * @param password
     * @param typeName
     * @param variables 要插入的参数
     * @param params 要进行修改的数据唯一编号
     * @return
     * @throws Exception
     */
    public void insertUpdateByGeoWfs(String businessId,String updateUser,String ip, String port, String username, String password, String typeName, JSONObject variables, Map<String, Object> params){
        taskExecutor.execute(()->{
            try {
                retryer.call(()->{
                    try {
                        this.send2GeoWfs(businessId,updateUser,ip, port, username, password, typeName, variables, params, InsertUpdateWfsXmlGenerate.class);
                        //成功则释放xmlString
                        this.xmlString = null;
                        return true;
                    }catch (GeoXmlQlException e){
                        log.error("请求失败,{}",e);
                        return false;
                    }
                });
            } catch (ExecutionException e) {
                e.printStackTrace();
                this.writeFile2Disk(this.path,this.xmlString);
                log.error("线程错误",e);
            } catch (RetryException e) {
                e.printStackTrace();
                log.error("重试错误",e);
                this.writeFile2Disk(this.path,this.xmlString);
            }
        });
    }

    private JSONObject send2GeoWfs(String businessId,String updateUser,String ip, String port, String username, String password, String typeName, JSONObject variables, Map<String, Object> params, Class<? extends WFSTXmlGenerate> c) throws Exception {
        HttpHeaders headers = this.createAuthorHeader(username, password);
        WFSTXmlGenerate generate = c.newInstance();
        Document document = generate.createXml(businessId,updateUser,typeName, "http://" + ip + ":" + port + "/geoserver/wfs", variables, params);
        log.info(document.asXML());
        this.xmlString = document.asXML();
        ResponseEntity<String> response = RestUtil.request("http://" + ip + ":" + port + "/geoserver/wfs", HttpMethod.POST, headers, null, document.asXML(), String.class);
        if(response.getStatusCode().is4xxClientError()){
            throw new GeoXmlQlException("host无法请求!");
        }
        if(response.getStatusCode().is5xxServerError()){
            throw new GeoXmlQlException("系统错误!");
        }
        if(response.getStatusCode().is2xxSuccessful() && response.getBody().contains("SUCCESS")){
            return XMLUtils.xmltoJson(response.getBody());
        }
        throw new GeoXmlQlException("请求错误!");
    }

}


对外rest API 接口

import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.utils.GeoToolsManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.*;

/**
 * @author qinlei
 * @description todo
 * @date 2021/10/22 10:17
 */
@Slf4j
@Api(tags = "wfs-t操作geoserver")
@RestController
@RequestMapping("/wfs")
public class WFSController {

    @Autowired
    private GeoToolsManager geoToolsManager;


    @PutMapping("/{typeName}/{user}/{businessId}")
    public Result<?> updateGeoserverData(@PathVariable String typeName,
                                    @PathVariable String user,
                                    @PathVariable String businessId,
                                    @RequestBody JSONObject jsonObject) {
        try {
            String ip = jsonObject.getString("ip");
            String port = jsonObject.getString("port");
            String username = jsonObject.getString("username");
            String password = jsonObject.getString("password");
            JSONObject variables = jsonObject.getJSONObject("variables");
            HashMap<String, Object> params = jsonObject.getObject("params", HashMap.class);
            geoToolsManager.insertUpdateByGeoWfs(businessId,user,ip,port,username,password,typeName,variables,params);
            return Result.OK();
        } catch (Exception e) {
            log.error("重试失败", e);
            return Result.error("重试失败");
        }
    }
}

接口使用方式

请求方式: put

请求路径url : http://localhost:10000/st-cloud/wfs/tscloud:mixed/qinlei/4563215482120214

请求body

{
    "ip":"10.0.0.15",
    "port":"9095",
    "username":"admin",
    "password":"bych1234",
    "variables":{
        "geom":{
            "type":"Point",
            "coordinates":"108.2326554521,34.1215421254"
        },
        "objectid":{
            "type":"general",
            "value":"11"
        },
        "hjd_bh":{
            "type":"general",
            "value":"test"
        },
        "hj_type":{
            "type":"general",
            "value":"gx"
        },
        "hj_reason":{
            "type":"general",
            "value":"test-reason"
        }
    },
    "params":{
        "hj_reason":"test-reason2",
        "objectid":"11"
    }
}

生成的xml

<?xml version="1.0" encoding="UTF-8"?>
<wfs:Transaction
    xmlns:ogc="http://www.opengis.net/ogc"
    xmlns:wfs="http://www.opengis.net/wfs"
    xmlns:gml="http://www.opengis.net/gml"
    xmlns:xsi="http://www.opengis.net/xsi"
    xmlns:scloud="http://scloud" version="1.0.0" service="WFS" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd http://scloud http://10.0.0.15:9095/geoserver/wfs/DescribeFeatureType?typename=scloud:mixed">
    <wfs:Update typeName="scloud:mixed">
        <wfs:Property>
            <wfs:Name>uploader</wfs:Name>
            <wfs:Value>qinlei</wfs:Value>
        </wfs:Property>
        <wfs:Property>
            <wfs:Name>isvalid</wfs:Name>
            <wfs:Value>0</wfs:Value>
        </wfs:Property>
        <wfs:Property>
            <wfs:Name>uploadtime</wfs:Name>
            <wfs:Value>2021-10-26 11:05:20.48</wfs:Value>
        </wfs:Property>
        <ogc:Filter>
            <ogc:PropertyIsEqualTo>
                <ogc:PropertyName>scloud:hj_reason</ogc:PropertyName>
                <ogc:Literal>test-reason2</ogc:Literal>
            </ogc:PropertyIsEqualTo>
            <ogc:PropertyIsEqualTo>
                <ogc:PropertyName>scloud:objectid</ogc:PropertyName>
                <ogc:Literal>11</ogc:Literal>
            </ogc:PropertyIsEqualTo>
        </ogc:Filter>
    </wfs:Update>
    <wfs:Insert>
        <scloud:mixed>
            <scloud:geom>
                <gml:Point srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">
                    <gml:coordinates decimal="." cs="," ts=";">108.2326554521,34.1215421254</gml:coordinates>
                </gml:Point>
            </scloud:geom>
            <scloud:objectid>11</scloud:objectid>
            <scloud:hjd_bh>test</scloud:hjd_bh>
            <scloud:hj_type>gx</scloud:hj_type>
            <scloud:hj_reason>test-reason</scloud:hj_reason>
            <scloud:isvalid>1</scloud:isvalid>
            <scloud:surveyor>qinlei</scloud:surveyor>
            <scloud:surveydate>2021-10-26</scloud:surveydate>
            <scloud:index_id>4563215482120214</scloud:index_id>
        </scloud:mixed>
    </wfs:Insert>
</wfs:Transaction>

个人正在整理项目中用到的geotools,抽空本人会封装起来。 https://gitee.com/qlanto/geotools-kit

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qlanto

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值