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