JAVA 讀取shp數據,shp導入,導出工具

1.問題,在Gis的項目中我們會經常用到有關於shp的讀取,導入導出的功能,爲此公司大牛做了一個工具,簡化了很多操作,只要學會應用即可

2.使用

ShapeTools,工具類,裏面封裝了各種處理shp的方法
public class ShapeTools<T> {
    /**
     * shp文件定義字段時,字段名長度不能多於10個字符
     */
    private final int FEILD_LENGTH_LIMIT = 10;

    /**
     * shp文件定義字段時,空間屬性字段必須是the_geom
     */
    private final String GEOM_FIELD_NAME = "the_geom";

    /**
     * 2017年11月23日
     * 讀取shp文件,把名稱和fid保存到ES中,
     * @param fileName
     * @param url 例: http://localhost:9200/dmdz2017/poi/_bulk </br>
     *            通過http  _bulk命令批量建索引的方式
     * @throws MalformedURLException
     * @throws IOException
     */
    public void readShpToES(String fileName, String url) throws MalformedURLException, IOException {
        ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
        WKTReader reader = new WKTReader();
        File file = new File(fileName);
        ShapefileDataStore sds = (ShapefileDataStore) dataStoreFactory.createDataStore(file.toURI().toURL());
        sds.setCharset(Charset.forName("GBK"));
        List<Map<String, String>> fs = new ArrayList<>();
        SimpleFeatureSource featureSource = sds.getFeatureSource();
        SimpleFeatureIterator iterator = featureSource.getFeatures().features();
        Property per = null;
        while(iterator.hasNext()){
            SimpleFeature feature = iterator.next();
            Iterator<Property> it = feature.getProperties().iterator();
            Map<String, String> properties = new HashMap<>();
            while(it.hasNext()){
                per = it.next();
                String key = per.getName().toString();
                properties.put(key, per.getValue().toString());
            }
            fs.add(properties);
        }
        shpToES(fs, url);
    }

    /**
     * 文件名加上時間戳
     * @param fname
     * @return
     */
    private String rename(String fname){
        int index = fname.lastIndexOf(".");

        String temp = fname.substring(0, index) + System.currentTimeMillis();
        temp += fname.substring(index);

        return temp;
    }

    /**
     * 使用Apache http建立ES索引
     * @param fs
     * @param url http://localhost:9200/dmdz2017/poi/_bulk
     * @return
     * @throws IOException
     */
    private boolean shpToES(List<Map<String, String>> fs, String url) throws IOException{
        if(null == url || "".equals(url)) return false;
        long startTime = System.currentTimeMillis();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        CloseableHttpClient httpclient = HttpClients.custom().setConnectionManager(cm).build();
        HttpPost httpPost = new HttpPost(url);
        StringBuilder sb = new StringBuilder();
        //
        Map<String, String> prs = null;
        for(int i = 0, total = fs.size(); i < total; i++){
            prs = fs.get(i);
            sb.append("{\"create\":{\"_id\":\"").append(startTime + i).append("\"}}\n");
            sb.append(toJson(prs)).append("\n");
            //System.out.println(sb.toString());
            if((i % 200) == 0 || i == total-1){
                httpPost.setEntity(new StringEntity(sb.toString(), "utf-8"));
                CloseableHttpResponse response = httpclient.execute(httpPost);
                response.close();
                sb = null;
                sb = new StringBuilder();
                System.out.println("數據入庫完成,總共"+total+",已完成" + i + ", 耗時 " + (System.currentTimeMillis() - startTime) + "毫秒");
            }
        }
        httpclient.close();
        return true;
    }

    private String toJson(Map<String, String> ms){
        if(null == ms) {
            return "{}";
        }
        StringBuilder sb = new StringBuilder("{");
        for(Iterator<String> iterator = ms.keySet().iterator(); iterator.hasNext(); sb.append(",")){
            String k = iterator.next();
            sb.append("\"").append(k).append("\":\"")
                    .append(ms.get(k)).append("\"");
        }
        sb.deleteCharAt(sb.lastIndexOf(","));
        sb.append("}");
        return sb.toString();
    }


    /**
     * 一個測試創建shp的方法
     * @throws SchemaException
     * @throws ParseException
     * @throws IOException
     */
    @Deprecated
    protected void testCreateFeature() throws SchemaException, ParseException, IOException{
        final SimpleFeatureType featureType = DataUtilities.createType("Location",
                "the_geom:MultiPolygon:4490,"
                        + "name:String,"
                        + "type:String,"
                        + "typeCode:String");

        List<SimpleFeature> features = new ArrayList<>();

        SimpleFeatureBuilder sfb = new SimpleFeatureBuilder(featureType);
        WKTReader wktReader = new WKTReader();
        Geometry g = wktReader.read("MULTIPOLYGON(((120.245550628244 30.3697319771691,120.245604273926 30.3693135531255,120.24339413664 30.3690667936267,120.243630170808 30.368079739938,120.24205303449 30.3678007917758,120.241409305793 30.3668673845079,120.241291288517 30.3659447050454,120.241838457153 30.3652902449766,120.24261093291 30.3647216149009,120.242857696301 30.3633590530615,120.242986442154 30.3626831362105,120.246097798899 30.3664382241636,120.249713410056 30.3696568698573,120.249788512627 30.3709336004773,120.249482741414 30.3710677121942,120.247229688992 30.3708906898944,120.247353069806 30.3699948315325,120.245513078403 30.3698285368174,120.245550628244 30.3697319771691,120.245550628244 30.3697319771691)),((120.250083554427 30.3672053292808,120.250094283151 30.367993898842,120.249713410056 30.3696568698573,120.247953884385 30.3681011910327,120.24685954468 30.3670712245555,120.247661524439 30.3666179298771,120.248780002808 30.365907142485,120.249450554592 30.366513321681,120.250083554427 30.3672053292808,120.250083554427 30.3672053292808,120.250083554427 30.3672053292808)))");
        sfb.add(g);
        sfb.add("ming cheng");
        sfb.add("lei xing");
        sfb.add("type code");
        features.add(sfb.buildFeature("idididi"));
        ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();

        Map<String, Serializable> params = new HashMap<>();
        File f = new File("d:\\test.shp");
        params.put("url", f.toURI().toURL());
        params.put("create spatial index", Boolean.TRUE);

        ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
        newDataStore.createSchema(featureType);
        String typeName = newDataStore.getTypeNames()[0];
        SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);

        SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;

        SimpleFeatureCollection collection = new ListFeatureCollection(featureType, features);
        Transaction transaction = new DefaultTransaction("create");
        featureStore.setTransaction(transaction);
        featureStore.addFeatures(collection);
        transaction.commit();

    }

    /**
     * 指定創建shp文件的字段屬性,注意字段名稱長度不能超過10個字符</br>
     * 2018年11月2日,除了空間屬性外,此方法只能生成全字符類型的其他屬性
     * @param heads
     * @param geomName
     * @param geomClass
     * @return
     */
    @Deprecated
    public SimpleFeatureTypeBuilder createFeatureTypeBuilder(String[] heads, String geomName, Class geomClass){
        SimpleFeatureTypeBuilder sftb = new SimpleFeatureTypeBuilder();
        for(String name : heads){
            if(name.equals(geomName)){
                sftb.add(GEOM_FIELD_NAME, geomClass);
            } else {
                sftb.add(name, String.class);
            }
        }
        return sftb;
    }

    /**
     * 2018年10月29日
     * 如果是爲了生成shp文件而調用此方法,注意字段名稱長度不能超過10個字符
     * @param clazz 有com.trgis.geotools.GeoJSON註解的空間類
     * @return
     */
    public SimpleFeatureTypeBuilder createFeatureTypeBuilder(Class clazz){
        SimpleFeatureTypeBuilder sftb = new SimpleFeatureTypeBuilder();
        String name = ((GeoJSON)clazz.getAnnotation(GeoJSON.class)).name();
        if(null == name){
            sftb.setName("feature");
        } else {
            sftb.setName(name);
        }
        boolean hasGeometry = false;
        Field[] fs = clazz.getDeclaredFields();
        for(Field f : fs){
            GeoJSON g = f.getAnnotation(GeoJSON.class);
            if(null == g)
                continue;
            String field = g.field();
            if("".equals(field)){
                field = f.getName();
            }
            if(!hasGeometry && g.isGeometry()) {
                hasGeometry = true;
                field = GEOM_FIELD_NAME;
            }
            Class type = g.type();
            if(type == Object.class)
                sftb.add(field, f.getType());
            else
                sftb.add(field, type);
        }
        if(!hasGeometry)
            throw new NullPointerException("GeoJSON : annotation must has geometry field");
        return sftb;
    }

    /**
     * 將封裝好的空間數據及屬性寫入到shp文件
     * @param tb
     * @param list 每個屬性對應的數據對象,屬性名稱不能超過10個字符
     * @param filePath
     * @return
     * @throws IOException
     */
    public boolean writeToShpFile(SimpleFeatureTypeBuilder tb, List<Map<String, Object>> list, String filePath) throws Exception {
        boolean b = false;
        Map<String, Serializable> params = new HashMap<String, Serializable>();
        FileDataStoreFactorySpi factory = new ShapefileDataStoreFactory();
        params.put(ShapefileDataStoreFactory.URLP.key,
                new File(filePath).toURI().toURL());
        ShapefileDataStore ds = (ShapefileDataStore) factory.createNewDataStore(params);
        tb.setName("shapefile");
        SimpleFeatureType sft = tb.buildFeatureType();
        ds.createSchema(sft);
        ds.setCharset(Charset.forName("GBK"));
        FeatureWriter<SimpleFeatureType, SimpleFeature> writer = ds.getFeatureWriter(
                Transaction.AUTO_COMMIT);
        //list中對象寫入feature對象
        for (int i = 0; i < list.size(); i++){
            SimpleFeature feature = writer.next();
            for(Map.Entry<String, Object> entry : list.get(i).entrySet()){
                if(entry.getKey().length() > FEILD_LENGTH_LIMIT) throw new Exception("定義shp文件的屬性字段名不能超過10個字符:" + entry.getKey());
                if(null != feature.getProperty(entry.getKey()))
                    feature.setAttribute(entry.getKey(), entry.getValue());
            }
        }
        writer.write();
        writer.close();
        ds.dispose();
        b = true;
        return b;
    }

    /**
     * 將空間對象集合保存到shp文件,空間對象必須是有GeoJSON註解的
     * 2018年11月1日
     * @param objs
     * @param filePath
     * @return
     * @throws Exception
     * @author 王風雨
     */
    public boolean writeToShpFromGeoJSON (List<T> objs, String filePath) throws Exception {
        boolean b = false;
        if(objs.size() == 0)
            return b;
        Class clazz = objs.get(0).getClass();
        GeoJSON g = objs.get(0).getClass().getAnnotation(GeoJSON.class);
        if(null == g)
            return b;
        SimpleFeatureTypeBuilder simpleFeatureTypeBuilder = new SimpleFeatureTypeBuilder();
        simpleFeatureTypeBuilder.setName(g.name());
        boolean hasGeometry = false;
        Field[] fields = clazz.getDeclaredFields();
        Map<String, String> fieldMap = new HashMap<>();
        for(Field f : fields){
            g = f.getAnnotation(GeoJSON.class);
            if(null == g)
                continue;
            String field = g.field();
            if("".equals(field)){
                field = f.getName();
            }
            if(field.length() > FEILD_LENGTH_LIMIT)
                throw new Exception("定義shp文件的屬性字段名不能超過10個字符" + field);
            if(!hasGeometry && g.isGeometry()) {
                hasGeometry = true;
                field = GEOM_FIELD_NAME;
            }
            Class type = g.type();
            if(type == Object.class)
                simpleFeatureTypeBuilder.add(field, f.getType());
            else
                simpleFeatureTypeBuilder.add(field, type);

            fieldMap.put(field, f.getName());
        }
        if(!hasGeometry)
            throw new NullPointerException("GeoJSON : annotation must has geometry field");
        List<Map<String, Object>> list = new ArrayList<>();
        for(T t : objs){
            Map<String, Object> obj = new HashMap<>();
            for(Map.Entry<String, String> fieldEntry : fieldMap.entrySet()){
                Method method = clazz.getMethod(getMethodName(fieldEntry.getValue()));
                obj.put(fieldEntry.getKey(), method.invoke(t));
            }
            list.add(obj);
        }
        b = writeToShpFile(simpleFeatureTypeBuilder, list, filePath);
        return b;
    }

    /**
     * 讀取shp文件,返回對象集合
     * @param filePath 文件名及全路徑
     * @param shpEntityBuilder feature對象轉換成指定對象的接口
     * @return
     * @throws Exception
     */
    public List<T> readShp(String filePath, ShpEntityBuilder<T> shpEntityBuilder) throws Exception {
        ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
        File file = new File(filePath);
        ShapefileDataStore sds = (ShapefileDataStore) dataStoreFactory.createDataStore(file.toURI().toURL());
        sds.setCharset(Charset.forName("GBK"));
        SimpleFeatureIterator iterator = sds.getFeatureSource().getFeatures().features();
        List<T> datas = new ArrayList<>();
        while (iterator.hasNext()){
            T data = shpEntityBuilder.createEntity(iterator.next());
            if(null != data)
                datas.add(data);
        }
        return datas;
    }

    /**
     * 將有GeoJSON註解的空間對象轉換成SimpleFeature
     * @param obj 只針對有GeoJSON註解的字段
     * @return
     * @throws IOException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     */
    public SimpleFeature toSimpleFeature(Object obj) throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        Class clazz = obj.getClass();
        SimpleFeatureTypeBuilder simpleFeatureTypeBuilder = createFeatureTypeBuilder(clazz);
        SimpleFeatureBuilder simpleFeatureBuilder = new SimpleFeatureBuilder(simpleFeatureTypeBuilder.buildFeatureType());
        String id = null;
        Map<String, Object> values = new HashMap<>();
        Field[] fields = clazz.getDeclaredFields();
        for(Field field : fields){
            GeoJSON g = field.getAnnotation(GeoJSON.class);
            if(null == g)
                continue;
            if(g.isID()){
                Method method = clazz.getMethod(getMethodName(field.getName()));
                id = method.invoke(obj).toString();
            } else {
                String fieldName =  g.field();
                if("".equals(fieldName)){
                    fieldName = field.getName();
                }
                if(g.isGeometry()){
                    fieldName = GEOM_FIELD_NAME;
                }
                Method method = clazz.getMethod(getMethodName(field.getName()));
                values.put(fieldName, method.invoke(obj));
            }
        }
        if(null == id)
            id = String.valueOf(System.currentTimeMillis());
        SimpleFeature feature = simpleFeatureBuilder.buildFeature(id);
        for (Map.Entry<String, Object> entry : values.entrySet()){
            feature.setAttribute(entry.getKey(), entry.getValue());
        }
        return feature;
    }

    /**
     * 將空間類集合轉換爲FeatureCollection對象
     * @param list 必須是有GeoJSON註解的空間類對象集合
     * @return
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws IOException
     */
    public SimpleFeatureCollection toSimpleFeatureCollection(List<Object> list) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, IOException {
        DefaultFeatureCollection featureCollection = new DefaultFeatureCollection();
        for(Object obj : list){
            featureCollection.add(toSimpleFeature(obj));
        }
        return featureCollection;
    }

    /**
     * 將有GeoJSON註解的空間對象轉換成GeoJSON格式字符串
     * @param obj 只針對有GeoJSON註解的字段
     * @return
     * @throws IOException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     */
    public String toGeoJSON(Object obj) throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        SimpleFeature feature = toSimpleFeature(obj);
        StringWriter writer = new StringWriter();
        org.geotools.geojson.GeoJSON.write(feature, writer);
        return writer.toString();
    }

    private String getMethodName(String field){
        char first = field.charAt(0);
        String str = String.valueOf(first).toUpperCase();
        String name = field.replaceFirst(String.valueOf(first), str);
        return "get" + name;
    }

    /**
     * 由Feature對象生成指定對象的接口,get Feature對象的屬性值 set 到指定對象,前提是知道屬性名稱和數據類型的對應關係
     * 例如 poi.setName(feature.getAttribute("name"))
     * @param <T>
     */
    public interface ShpEntityBuilder<T> {
        /**
         * 根據字段對應關係,生成指定對象
         * @param feature
         * @return
         * @throws Exception
         */
        public T createEntity(SimpleFeature feature) throws Exception;
    }

    private  String getSetMethodName(String field){
        char first = field.charAt(0);
        String str = String.valueOf(first).toUpperCase();
        String name = field.replaceFirst(String.valueOf(first), str);
        return "set" + name;
    }

    /**
     * 2018年11月2日
     * 傳遞GeoJSON註釋的類,使用註解的對應關係將shp文件讀取的數據轉換成指定對象
     * @param clazz
     * @return
     */
    public ShpEntityBuilder<T> useDefaultBuilder(Class clazz) {
        return feature -> {
            GeoJSON g = (GeoJSON) clazz.getAnnotation(GeoJSON.class);
            if(null == g)
                throw new Exception("非GeoJSON註解的類");
            T t = (T)clazz.newInstance();
            Field[] fields = clazz.getDeclaredFields();
            for(Field f : fields){
                g = f.getAnnotation(GeoJSON.class);
                if(null == g)
                    continue;
                Method method = clazz.getMethod(getSetMethodName(f.getName()), f.getType());
                method.invoke(t, feature.getAttribute(g.isGeometry() ? GEOM_FIELD_NAME : "".equals(g.field()) ? f.getName() : g.field()));
            }
            return t;
        };
    }
}
GeoJSON類 空間對象進行GeoJSON格式字符化
package com.trgis.geotools;

import java.lang.annotation.*;

/**
 * ShapeTools工具類的toGeoJSON方法對空間對象進行GeoJSON格式字符化時使用</br>
 * 用來生成org.geotools.feature.simple.SimpleFeatureTypeBuilder
 * 2018年10月29日
 * @author 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
@Documented
@Inherited
public @interface GeoJSON {
    /**
     * 對應的SimpleFeatureTypeBuilder的setName方法參數,一般用在類上
     *
     * @return
     */
    public String name() default "feature";

    /**
     * 用在空間對象的屬性上,對應GeoJSON的屬性名稱</br>
     * 因爲生成shp文件時,定義的shp字段名不能超過10個字符</br>
     * 所以當對象數據名長度超過10個字符時,必須指定field
     * @return
     */
    public String field() default "";

    /**
     * 在空間屬性上使用應設置爲true,且field需要設置爲the_geom
     * @return
     */
    public boolean isGeometry() default false;

    /**
     * ID屬性上使用應設置爲true
     * @return
     */
    public boolean isID() default false;

    /**
     * 對象的Class類型,當int, double等時,需要聲明爲Integer,Double等</br>
     * <code>@GeoJSON(type=Integer.class)</code>
     * <code>int count</code>
     * @return
     */
    public  Class type() default  Object.class;
}

poi類

package com.trgis.geotools.model;

import com.trgis.geotools.GeoJSON;
import com.vividsolutions.jts.geom.Point;

/**
 * 2018/11/2
 *
 * @author 
 */
@GeoJSON(name = "poi")
public class Poi {
    /**
     * 類型是int double等時,必須指定type爲包裝類
     */
    @GeoJSON(isID = true, type = Integer.class)
    private int id;

    @GeoJSON(isGeometry = true)
    private Point location;

    @GeoJSON
    private String name;

    /**
     * 字段名稱超過10個字符時,生成shp字段定義會出錯
     */
    @GeoJSON(field = "the_pro", type = Double.class)
    private double the_properties;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Point getLocation() {
        return location;
    }

    public void setLocation(Point location) {
        this.location = location;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getThe_properties() {
        return the_properties;
    }

    public void setThe_properties(double the_properties) {
        this.the_properties = the_properties;
    }
}

測試類

 /**
     * 讀取shp文件,封裝成空間對象
     * @throws Exception
     */
    @Test
    public void testBouFromShp() throws Exception {
        String shpFile = "src\\main\\resources\\shp\\jiedao.shp";

        //工具方法一readShp,從shp文件讀取,封裝成空間對象
        List<GovBou> objs = shapeTools.readShp(shpFile, feature -> {
            GovBou bou = new GovBou();
            //shp文件的所有屬性:the_geom, FNAME, FEATUREGUI, SHAPE_Leng, SHAPE_Area
            Object geom = feature.getAttribute("the_geom");
            String name = feature.getAttribute("FNAME").toString();
            if(null != geom && geom instanceof MultiPolygon){
                bou.setBou((MultiPolygon) geom);
            }
            bou.setName(name);
            bou.setId(String.valueOf(System.currentTimeMillis()));
            return bou;
        });

        System.out.println("讀取個數:" + objs.size());

        //工具方法二toGeoJSON,空間對象轉換層GeoJSON格式字符串,通過註解自動生成
        String geojson = shapeTools.toGeoJSON(objs.get(0));
        System.out.println(geojson);

    }

    /**
     * 從shp文件獲取數據到對象也可以調用這個方法,利用類上的GeoJSON註解進行屬性對應
     */
    @Test
    public void testBouFromShp2() throws Exception {
        String shpFile = "src\\main\\resources\\shp\\jiedao.shp";
        List<GovBou> govBouList = shapeTools.readShp(shpFile, shapeTools.useDefaultBuilder(GovBou.class));
        System.out.println("獲取到Bou對象總數:"+govBouList.size());
    }
 /**
     * 工具方法五:空間對象保存成shp文件
     * @throws Exception
     */
    @Test
    public void testWriteToShp2() throws Exception {
        WKTReader reader = new WKTReader();
        List<Poi> poiList = new ArrayList<>();
        Poi p1 = new Poi();
        p1.setId(1);
        p1.setName("aaa");
        p1.setThe_properties(234.567);
        p1.setLocation((Point)reader.read("POINT(120.12 30.421)"));
        poiList.add(p1);

        String filePath = "src\\main\\resources\\shp\\poi1.shp";
        boolean result = shapeTools.writeToShpFromGeoJSON(poiList, filePath);
        System.out.println(result ? "保存shp文件成功" :  "失敗");

        //驗證一下從生成的shp文件讀取
        List<Poi> pois = shapeTools.readShp(filePath, shapeTools.useDefaultBuilder(Poi.class));
        if(pois.size() > 0){
            Poi p = pois.get(0);
            System.out.println(shapeTools.toGeoJSON(p));
        }
    }

3.coding 地址:coding

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章