geotools是java语言封装的空间数据框架。类似于spring之与java web项目,geotools提供丰富的GIS组件,可用于快速二次搭建GIS平台。详细可登录geotools官网了解其功能信息(https://geotools.org/)。
postgis是postgresql数据库的扩展,可以理解为postgresql的GIS化功能封装。
本博文用postgresql 11和postgis 3.1.1版本给大家演示
数据源获取,其原理于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;
}
}
本文使用单一简单要素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
空间表数据增删改涉及到数据库事务,切记事务的提交,也可设置事务自动提交。参考类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();
}
}
}
}
插入结果展示
空间数据查询可以用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
查询结果展示:
最后我们讨论ol与geotools的相似点,拿openlayers 3以上版本作比较。GIS前端框架有诸如arcgis js api,leaflet,Mapbox-gl等,我们为何选择openlayers做示例呢。
如果熟悉ol框架的博友会发现,geotools的设计架构、理念和思路,与ol框架非常相似。只不过ol是GIS前端框架,可用于展示层,geotools作为服务端框架,用于数据层,交互层。熟悉ol框架架构,对于geotools源码的阅读有极大的帮助。
举个栗子:
使用过ol框架的博友,对比其他GIS前端框架,会发现ol完全适配与geoserver服务,而geoserver底层框架依赖于geotools。
可以得出熟悉ol对理解geotools大有好处。
本文带大家解析geotools源码,只是postgis篇,后续会解析其他板块,敬请期待和支持。可能有不合理的地方,希望大家不辞指正,或者评论区留言。示例源码已上传码云gitee服务器,大家可以自行下载调试
下载地址:https://gitee.com/yangdengxian/geodatastore.git