Java中使用GeoTools导出Shape文件压缩包(Web端)


 

 

Web端Java中使用GeoTools导出Shape文件压缩包,包含.dbf; .fix; .prj; .shp; .shx

 

  1. Sql 查询数据库中的需要导出的数据 geometry字段需要加上 ST_asText( )

     例如:
    select g.name,ST_asText(g.geom) as "geomText" from tb_geometry g

     

    List<Map<String, Object>> selectGeometryForShape();
  2. 导出ShapeFile工具类

    import cn.hutool.core.collection.CollUtil;
    import cn.hutool.core.util.StrUtil;
    
    import org.geotools.data.DataUtilities;
    import org.geotools.data.DefaultTransaction;
    import org.geotools.data.Transaction;
    import org.geotools.data.collection.ListFeatureCollection;
    import org.geotools.data.shapefile.ShapefileDataStore;
    import org.geotools.data.shapefile.ShapefileDataStoreFactory;
    import org.geotools.data.simple.SimpleFeatureSource;
    import org.geotools.data.simple.SimpleFeatureStore;
    import org.geotools.feature.simple.SimpleFeatureBuilder;
    import org.geotools.referencing.crs.DefaultGeographicCRS;
    import org.locationtech.jts.geom.Geometry;
    import org.locationtech.jts.geom.Point;
    import org.locationtech.jts.io.WKTReader;
    import org.opengis.feature.simple.SimpleFeature;
    import org.opengis.feature.simple.SimpleFeatureType;
    
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
    import java.io.*;
    import java.net.URLEncoder;
    import java.nio.charset.Charset;
    import java.util.*;
    
    /**
     * @author LiuXing
     * @date 2022-01-10
     */
    public class ExportShapeFileUtil {
    
        static String BLANK = " ";
    
        /**
         * 根据ListMap导出完整shape
         *
         * @param response response
         * @param list     listMap
         * @return boolean
         */
        public static boolean exportCompleteShape(HttpServletResponse response, List<Map<String, Object>> list) {
    
            boolean flag = false;
    
            FileOutputStream fileOutputStream = null;
            String id = String.valueOf(System.currentTimeMillis());
            String tempPath = System.getProperty("catalina.home") + "/temp/" + id + "/";
            //临时文件
            String shpFilePath = tempPath + id + ".shp";
            //压缩包,把xls文件压缩到zip中,在浏览器下载
            String zipPath = System.getProperty("catalina.home") + "/temp/" + id + ".zip";
            File dir = new File(tempPath);
            //每目录就新建
            if (!dir.exists()) {
                boolean mkdirs = dir.mkdirs();
            }
            //把生成的文件压缩为.zip的
            File zipFile = new File(zipPath);
            File shpFile = new File(shpFilePath);
    
            try {
    
                //处理点线面
                Map<String, Object> map = list.get(0);
                String geomText = map.get("geomText").toString();
                String featureTypeStr = SubStringUtils.subStr(geomText, 0, StrUtil.indexOf(geomText, '('));
                if (StrUtil.containsAnyIgnoreCase(featureTypeStr, BLANK)) {
                    featureTypeStr = SubStringUtils.subStr(featureTypeStr, 0, StrUtil.indexOf(featureTypeStr, ' '));
                }
    
                if (StrUtil.equalsIgnoreCase(featureTypeStr, GeomFormerType.POINT.value)) {
                    featureTypeStr = GeomTitleTypeEnum.POINT.value;
                } else if (StrUtil.equalsIgnoreCase(featureTypeStr, GeomFormerType.LINESTRING.value)) {
                    featureTypeStr = GeomTitleTypeEnum.LINESTRING.value;
                } else if (StrUtil.equalsIgnoreCase(featureTypeStr, GeomFormerType.POLYGON.value)) {
                    featureTypeStr = GeomTitleTypeEnum.POLYGON.value;
                } else if (StrUtil.equalsIgnoreCase(featureTypeStr, GeomFormerType.MULTILINESTRING.value)) {
                    featureTypeStr = GeomTitleTypeEnum.MULTILINESTRING.value;
                } else if (StrUtil.equalsIgnoreCase(featureTypeStr, GeomFormerType.MULTIPOLYGON.value)) {
                    featureTypeStr = GeomTitleTypeEnum.MULTIPOLYGON.value;
                }
    
                //构建typeSpec
                StringBuilder typeSpecStringBuilder = new StringBuilder();
                typeSpecStringBuilder.append("the_geom:" + featureTypeStr + ":4326,");
                assert CollUtil.isNotEmpty(list);
                Map<String, Object> stringObjectMap = list.get(0);
                for (String field : stringObjectMap.keySet()) {
                    if (!StrUtil.equals("geomText", field) && !StrUtil.equals("gid", field) && !StrUtil.equals("geom", field)) {
                        typeSpecStringBuilder.append(field).append(",");
                    }
                }
                String typeSpec = typeSpecStringBuilder.substring(0, typeSpecStringBuilder.length() - 1);
                final SimpleFeatureType featureType = DataUtilities.createType("Location", typeSpec);
    
                //处理shape数据
                List<SimpleFeature> features = new ArrayList<>();
                ListFeatureCollection collection = new ListFeatureCollection(featureType, features);
                SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
                for (Map<String, Object> row : list) {
                    String wkt = row.get("geomText").toString();
                    //WKT转Geometry
                    WKTReader wktReader = new WKTReader();
                    Geometry geometry = null;
                    try {
                        geometry = wktReader.read(wkt);
                        featureBuilder.add(geometry);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    for (String colum : row.keySet()) {
                        if (!StrUtil.equals("geomText", colum) && !StrUtil.equals("gid", colum) && !StrUtil.equals("geom", colum)) {
                            featureBuilder.add(row.get(colum));
                        }
                    }
                    SimpleFeature feature = featureBuilder.buildFeature(null);
                    collection.add(feature);
                }
    
                ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
                Map<String, Serializable> params = new HashMap<String, Serializable>();
                params.put("url", shpFile.toURI().toURL());
                params.put("create spatial index", Boolean.TRUE);
                ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
                //处理中文乱码
                newDataStore.setCharset(Charset.forName("GBK"));
                newDataStore.createSchema(featureType);
                newDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);
                Transaction transaction = new DefaultTransaction("create");
                String typeName = newDataStore.getTypeNames()[0];
                SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);
    
                if (featureSource instanceof SimpleFeatureStore) {
                    SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
                    featureStore.setTransaction(transaction);
                    try {
                        featureStore.addFeatures(collection);
                        transaction.commit();
                    } catch (Exception problem) {
                        problem.printStackTrace();
                        transaction.rollback();
                    } finally {
                        transaction.close();
                    }
                }
    
                FileOutputStream stream = new FileOutputStream(zipFile);
                ZipUtils.toZip(tempPath, stream, true);
                // 读到流中
                //文件的存放路径
                InputStream inStream = new FileInputStream(zipPath);
                // 设置输出的格式
                response.reset();
                response.setContentType("application/bin");
                response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipFile.getName(), "UTF-8"));
                ServletOutputStream outputStream = response.getOutputStream();
                // 循环取出流中的数据
                byte[] b = new byte[1024];
                int len;
                while ((len = inStream.read(b)) > 0) {
                    outputStream.write(b, 0, len);
                }
                inStream.close();
                outputStream.flush();
                outputStream.close();
                flag = true;
            } catch (Exception ignored) {
            } finally {
                if (fileOutputStream != null) {
                    try {
                        fileOutputStream.close();
                    } catch (Exception ignored) {
                    }
                }
                File fileDir = new File(tempPath);
                if (fileDir.exists() && fileDir.isDirectory()) {
                    File[] files = fileDir.listFiles();
                    assert files != null;
                    for (File f : files) {
                        if (f.exists()) {
                            boolean delete = f.delete();
                        }
                    }
                    boolean delete = fileDir.delete();
                }
                if (zipFile.exists()) {
                    boolean delete = zipFile.delete();
                }
            }
            return flag;
        }
    }
  3. 其他辅助工具类

    3.1 压缩包工具类

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipOutputStream;
    
    /**
     * 压缩文件Utils
     *
     * @date 2022-01-10
     */
    public class ZipUtils {
        private static final int BUFFER_SIZE = 2 * 1024;
        /**
         * 压缩成ZIP 方法1
         *
         * @param srcDir           压缩文件夹路径
         * @param out              压缩文件输出流
         * @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
         *                         <p>
         *                         false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
         * @throws RuntimeException 压缩失败会抛出运行时异常
         */
        public static void toZip(String srcDir, OutputStream out, boolean keepDirStructure) throws RuntimeException {
    
            ZipOutputStream zos = null;
            try {
                zos = new ZipOutputStream(out);
                File sourceFile = new File(srcDir);
                compress(sourceFile, zos, sourceFile.getName(), keepDirStructure);
            } catch (Exception e) {
                throw new RuntimeException("zip error from ZipUtils", e);
            } finally {
                if (zos != null) {
                    try {
                        zos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    
        /**
         * 递归压缩方法
         *
         * @param sourceFile       源文件
         * @param zos              zip输出流
         * @param name             压缩后的名称
         * @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
         *                         <p>
         *                         false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
         */
    
        private static void compress(File sourceFile, ZipOutputStream zos, String name, boolean keepDirStructure) throws Exception {
            byte[] buf = new byte[BUFFER_SIZE];
            if (sourceFile.isFile()) {
                // 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
                zos.putNextEntry(new ZipEntry(name));
                // copy文件到zip输出流中
                int len;
                FileInputStream in = new FileInputStream(sourceFile);
                while ((len = in.read(buf)) != -1) {
                    zos.write(buf, 0, len);
                }
                // Complete the entry
                zos.closeEntry();
                in.close();
            } else {
                File[] listFiles = sourceFile.listFiles();
                if (listFiles == null || listFiles.length == 0) {
                    // 需要保留原来的文件结构时,需要对空文件夹进行处理
                    if (keepDirStructure) {
                        // 空文件夹的处理
                        zos.putNextEntry(new ZipEntry(name + "/"));
                        // 没有文件,不需要文件的copy
                        zos.closeEntry();
                    }
                } else {
                    for (File file : listFiles) {
                        // 判断是否需要保留原来的文件结构
                        if (keepDirStructure) {
                            // 注意:file.getName()前面需要带上父文件夹的名字加一斜杠,
                            // 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了
                            compress(file, zos, name + "/" + file.getName(), keepDirStructure);
                        } else {
                            compress(file, zos, file.getName(), keepDirStructure);
                        }
                    }
                }
            }
        }
    }

     

    3.2 方便处理点线面的枚举

    /**
     * Shape文件将要使用的类型
     *
     * @author LiuXing
     * @date 2022-01-11
     */
    
    public enum GeomTitleTypeEnum {
        /**
         * 点
         */
        POINT("Point"),
        /**
         * 线
         */
        LINESTRING("LineString"),
        /**
         * 面
         */
        POLYGON("Polygon"),
        /**
         * 复杂线
         */
        MULTILINESTRING("MultiLineString"),
        /**
         * 复杂面
         */
        MULTIPOLYGON("MultiPolygon");
    
        public String value;
    
        GeomTitleTypeEnum(String value) {
            this.value = value;
        }
    
        public String value() {
            return this.value;
        }
    }
    /**
     * 原始类型
     * @author LiuXing
     * @date 2022-01-11
     */
    
    public enum GeomFormerType {
        /**
         * 点
         */
        POINT("POINT"),
        /**
         * 线
         */
        LINESTRING("LINESTRING"),
        /**
         * 面
         */
        POLYGON("POLYGON"),
        /**
         * 复杂线
         */
        MULTILINESTRING("MULTILINESTRING"),
        /**
         * 复杂面
         */
        MULTIPOLYGON("MULTIPOLYGON");
    
        public String value;
    
        GeomFormerType(String value) {
            this.value = value;
        }
    
        public String value() {
            return this.value;
        }
    }
    

     

    3.3字符串切割工具

     

     

     

     

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * @author LiuXing
     * @date 2021-05-10
     */
    public class SubStringUtils {
    
        private static final Logger logger = LoggerFactory.getLogger(SubStringUtils.class);
    
        /**
         * @param strings  字符串数组
         * @param splitStr 连接数组的字符串
         * @Title: join
         * @Description: 用指定字符串数组相连接,并返回
         * @return: String
         */
        public static String join(String[] strings, String splitStr) {
            if (strings != null) {
                if (strings.length == 1) {
                    return strings[0];
                }
                StringBuffer sb = new StringBuffer();
                for (String str : strings) {
                    sb.append(str).append(splitStr);
                }
                if (sb.length() > 0) {
                    sb.delete(sb.length() - splitStr.length(), sb.length());
                }
                return sb.toString();
            }
            return null;
        }
    
        /**
         * @param str 字符串
         * @param end 结束位置
         * @Title: subStrStart
         * @Description: 从头开始截取
         * @return: String
         */
        public static String subStrStart(String str, int end) {
            return subStr(str, 0, end);
        }
    
        /**
         * @param str   字符串
         * @param start 开始位置
         * @Title: subStrEnd
         * @Description: 从尾开始截取
         * @return: String
         */
        public static String subStrEnd(String str, int start) {
            return subStr(str, str.length() - start, str.length());
        }
    
        /**
         * @param str    待截取的字符串
         * @param length 长度 ,>=0时,从头开始向后截取length长度的字符串;<0时,从尾开始向前截取length长度的字符串
         * @return
         * @throws RuntimeException
         * @Title: subStr
         * @Description: 截取字符串 (支持正向、反向截取)
         * @return: String
         */
        public static String subStr(String str, int length) throws RuntimeException {
            if (str == null) {
                logger.error("字符串为null");
                throw new NullPointerException("字符串为null");
    
            }
            int len = str.length();
            if (len < Math.abs(length)) {
                String msg = "最大长度为" + len + ",索引超出范围为:" + (len - Math.abs(length));
                logger.error(msg);
                throw new StringIndexOutOfBoundsException(msg);
            }
            if (length >= 0) {
                return subStr(str, 0, length);
            } else {
                return subStr(str, len - Math.abs(length), len);
            }
        }
    
    
        /**
         * 截取字符串 (支持正向、反向选择)
         *
         * @param str   待截取的字符串
         * @param start 起始索引 ,>=0时,从start开始截取;<0时,从length-|start|开始截取
         * @param end   结束索引 ,>=0时,从end结束截取;<0时,从length-|end|结束截取
         * @return 返回截取的字符串
         * @throws RuntimeException
         */
        public static String subStr(String str, int start, int end) throws RuntimeException {
            if (str == null) {
                logger.error("字符串为空");
                throw new NullPointerException("");
            }
            int len = str.length();
            //记录起始索引
            int s = 0;
            //记录结尾索引
            int e = 0;
            if (len < Math.abs(start)) {
                String msg = "最大长度为" + len + ",索引超出范围为:" + (len - Math.abs(start));
                logger.error(msg);
                throw new StringIndexOutOfBoundsException(msg);
            } else if (start < 0) {
                s = len - Math.abs(start);
            } else if (start < 0) {
                s = 0;
            } else {//>=0
                s = start;
            }
            if (len < Math.abs(end)) {
                String msg = "最大长度为" + len + ",索引超出范围为:" + (len - Math.abs(end));
                logger.error(msg);
                throw new StringIndexOutOfBoundsException(msg);
            } else if (end < 0) {
                e = len - Math.abs(end);
            } else if (end == 0) {
                e = len;
            } else {//>=0
                e = end;
            }
            if (e < s) {
                logger.error("截至索引小于起始索引:" + (e - s));
                throw new StringIndexOutOfBoundsException("截至索引小于起始索引:" + (e - s));
            }
            return str.substring(s, e);
        }
    
    
        public static void main(String[] args) {
            String str = "12345abcde";
            System.out.println("--------------------------------");
            System.out.println("正向截取长度为4,结果:\n" + SubStringUtils.subStr(str, 4));
            System.out.println("反向截取长度为4,结果:\n" + SubStringUtils.subStr(str, -4));
            System.out.println("--------------------------------");
            System.out.println("正向截取到第4个字符的位置,结果:\n" + SubStringUtils.subStrStart(str, 4));
            System.out.println("反向截取到第4个字符的位置,结果:\n" + SubStringUtils.subStrEnd(str, 4));
            System.out.println("--------------------------------");
            System.out.println("从第2个截取到第9个,结果:\n" + SubStringUtils.subStr(str, 1, 9));
            System.out.println("从第2个截取到倒数第1个,结果:\n" + SubStringUtils.subStr(str, 1, -1));
            System.out.println("从倒数第4个开始截取,结果:\n" + SubStringUtils.subStr(str, -4, 0));
            System.out.println("从倒数第4个开始截取,结果:\n" + SubStringUtils.subStr(str, -4, 10));
        }
    
    }
  4. 相关pom

    <properties>    
    		<geotools.version>26.1</geotools.version>
        </properties> 		
    
    		<dependency>
                <groupId>org.geotools</groupId>
                <artifactId>gt-shapefile</artifactId>
                <version>${geotools.version}</version>
            </dependency>
            <dependency>
                <groupId>org.geotools</groupId>
                <artifactId>gt-swing</artifactId>
                <version>${geotools.version}</version>
            </dependency>
            <dependency>
                <groupId>org.geotools</groupId>
                <artifactId>gt-geojson</artifactId>
                <version>${geotools.version}</version>
            </dependency>
            <dependency>
                <groupId>org.geotools.jdbc</groupId>
                <artifactId>gt-jdbc-postgis</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-opengis</artifactId>
                <version>${geotools.version}</version>
            </dependency>

 

 

    

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM