geotools源码解析——postgis篇

1. geotools简介

geotools是java语言封装的空间数据框架。类似于spring之与java web项目,geotools提供丰富的GIS组件,可用于快速二次搭建GIS平台。详细可登录geotools官网了解其功能信息(https://geotools.org/)。

2. geotools postgis源码解析

postgis是postgresql数据库的扩展,可以理解为postgresql的GIS化功能封装。

本博文用postgresql 11和postgis 3.1.1版本给大家演示

2.1. 用geotools连接postgis

数据源获取,其原理于JDBC数据库连接,在geotools中可称为初始化数据存储(geoserver借鉴于geotools概念)。可参考类DataStore,JDBCDataSotre。

package com.geodatastore.postgis;

import org.geotools.data.*;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.postgis.PostgisNGDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeImpl;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import org.opengis.filter.identity.FeatureId;

import java.awt.*;
import java.io.IOException;
import java.util.*;
import java.util.List;

public class PostgisDataStore {

    private DataStore dataStore = null;
    private GeometryFactory geometryFactory = null;
    private final String tableName = "pois";
    //获取类型
    private SimpleFeatureType type = null;
    public PostgisDataStore() {
        this.geometryFactory = new GeometryFactory();
    };

    public  DataStore getDataStore(Map map) throws IOException {
        Map params = new HashMap<>();
        params.put(PostgisNGDataStoreFactory.DBTYPE.key, "postgis");
        params.put(PostgisNGDataStoreFactory.HOST.key, map.get("host"));
        params.put(PostgisNGDataStoreFactory.PORT.key, 5432);
        params.put(PostgisNGDataStoreFactory.SCHEMA.key, map.get("schema"));
        params.put(PostgisNGDataStoreFactory.DATABASE.key, map.get("database"));
        params.put(PostgisNGDataStoreFactory.USER.key, map.get("user"));
        params.put(PostgisNGDataStoreFactory.PASSWD.key, map.get("passwd"));

        dataStore = DataStoreFinder.getDataStore(params);

        if (dataStore == null) {
            throw new IOException("数据库连接未成功");
        } else {
            System.out.println("数据库连接成功");
        }
        return dataStore;
    }
}

2.2. 创建矢量要素Feature

本文使用单一简单要素SimpleFeature给大家讲解,暂不解析复杂多点线面要素源码。以单点Point为例给大家解析,参考类Point,SimpleFeature,Geometry

public SimpleFeature createSimplePointFeatureByLonLat(Double lon,Double lat) throws IOException {
        SimpleFeatureSource simpleFeatureSource = null;

        Point point = null;
        //构建要素
        SimpleFeature feature = null;
        if (dataStore != null) {
            simpleFeatureSource = dataStore.getFeatureSource(tableName);
            type = simpleFeatureSource.getSchema();
            SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(type);
            point = geometryFactory.createPoint(new Coordinate(lon,lat));
            //属性值顺序与SimpleFeatureType对应
            List resultList = new ArrayList<>();
            resultList.add(point);
            resultList.add(5678);
            resultList.add("erftg");
            featureBuilder.addAll(resultList);

            feature = featureBuilder.buildFeature("poi1");

        } else {
            throw new IOException("数据库连接未成功");
        }
        return feature;
};

2.3 新增矢量数据

空间表数据增删改涉及到数据库事务,切记事务的提交,也可设置事务自动提交。参考类SimpleFeatureStore,SimpleFeatureCollection,Transaction。

public void  insertPointByLonLat(Double lon,Double lat) throws IOException {
        SimpleFeature feature = createSimplePointFeatureByLonLat(lon,lat);
        List features = new ArrayList<>();
        features.add(feature);
        SimpleFeatureSource featureSource = dataStore.getFeatureSource(tableName);

        if( featureSource instanceof SimpleFeatureStore){
            SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
            SimpleFeatureCollection featureCollection = new ListFeatureCollection(type,features);
            //创建事务
            Transaction session = new DefaultTransaction("Adding");
            featureStore.setTransaction( session );
            try {
                List added = featureStore.addFeatures( featureCollection );
                System.out.println( "Added "+added );
                //提交事务
                session.commit();
            }
            catch (Throwable t){
                System.out.println( "Failed to add features: "+t );
                try {
                    //事务回归
                    session.rollback();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
}

插入结果展示

2.4. 矢量数据查询

空间数据查询可以用CQL语法来封装查询条件,可封装普通where条件,也可封装空间分析(Contains,Within,BBox)等条件。参考类Query,CQL,SimpleFeatureCollection

public SimpleFeatureCollection queryFeatures(String cql_filter) throws Exception {
        SimpleFeatureSource featureSource = null;
        SimpleFeatureCollection featureCollection = null;
        Filter filter = CQL.toFilter(cql_filter);
        Query query = new Query("pois",filter);
        if (dataStore != null) {
            featureSource = dataStore.getFeatureSource(tableName);
            SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
            featureCollection = featureStore.getFeatures(query);
        } else {
            throw new Exception("数据库连接未成功");
        }
        return featureCollection;
}

public static void main(String[] args) throws Exception {
        PostgisDataStore postgisDataStore = new PostgisDataStore();
        Map params = new HashMap<>();
        params.put(PostgisNGDataStoreFactory.HOST.key, "localhost");
        params.put(PostgisNGDataStoreFactory.SCHEMA.key, "public");
        params.put(PostgisNGDataStoreFactory.DATABASE.key, "postgres");
        params.put(PostgisNGDataStoreFactory.USER.key, "postgres");
        params.put(PostgisNGDataStoreFactory.PASSWD.key,  "postgres");
        DataStore dataStore = postgisDataStore.getDataStore(params);

//        postgisDataStore.insertPointByLonLat(108.21,38.34);

        SimpleFeatureCollection featureCollection = postgisDataStore.queryFeatures("uid = 'abcdfs'");
        List attributes = new ArrayList<>();
        try (SimpleFeatureIterator iterator = featureCollection.features()) {
            while (iterator.hasNext()) {
                SimpleFeature feature = iterator.next();
                Geometry geom = (Geometry) feature.getDefaultGeometry();
                Point centroid = geom.getCentroid();
                attributes = feature.getAttributes();
            }
        }
}

查询结果展示:

geotools源码解析——postgis篇_第1张图片

3. openlayers与geotools对比

最后我们讨论ol与geotools的相似点,拿openlayers 3以上版本作比较。GIS前端框架有诸如arcgis js api,leaflet,Mapbox-gl等,我们为何选择openlayers做示例呢。

如果熟悉ol框架的博友会发现,geotools的设计架构、理念和思路,与ol框架非常相似。只不过ol是GIS前端框架,可用于展示层,geotools作为服务端框架,用于数据层,交互层。熟悉ol框架架构,对于geotools源码的阅读有极大的帮助。

举个栗子:

  1. ol的要素类,Feature和FeatureCollection,geotools的SimpleFeature,SimpleFeatureCollection类,其中的属性Geometry,Properties,方法如getID,getProperties等,可以说模式与设计二者一样。
  2. ol的数据查询类Filter,geotools的Query,Filter类,ol的CQL条件查询类IsNull,Like,BBox之于geotools的CQL类。
  3. ol的样式Style类之于geotools的Style类,文字Font,图片Image,颜色Color的渲染二者也是大同小异。

使用过ol框架的博友,对比其他GIS前端框架,会发现ol完全适配与geoserver服务,而geoserver底层框架依赖于geotools。

可以得出熟悉ol对理解geotools大有好处。

4. 总结

本文带大家解析geotools源码,只是postgis篇,后续会解析其他板块,敬请期待和支持。可能有不合理的地方,希望大家不辞指正,或者评论区留言。示例源码已上传码云gitee服务器,大家可以自行下载调试

下载地址:https://gitee.com/yangdengxian/geodatastore.git

 

你可能感兴趣的:(gis,postgis,java,postgresql,postgis,geodatabase)