前言
ShapeFile文件用于描述空间数据中的矢量数据。例如几何体对象:点,折线与多边形。以及同时存储几何形状与属性信息,这是Shapefile文件最基本的特征。
Shape File (以下简称shp文件)主要有三个主文件,及若干子文件组成的名称相同的文件。
1 . shpname.shp 用于保存元素的几何实体的,即主文件
2 . shpname.shx 图形索引格式 几何体位置索引,记录每一个几何体在shp文件之中的位置,能够加快向前或向后搜索一个几何体的效率。
3 . shpname.dbf 属性数据格式,以dBase IV的数据表格式存储每个几何形状的属性数据
还有更多的如:
.prj— 投帧式,用于保存地理坐标系统与投影信息,是一个存储well-known text投影描述符的文本文件。
.sbnand.sbx— 几何体的空间索引
.fbnand.fbx— 只读的Shapefiles的几何体的空间索引
.ainand.aih— 列表中活动字段的属性索引。
.ixs— 可读写Shapefile文件的地理编码索引
.mxs— 可读写Shapefile文件的地理编码索引(ODB格式)
.dbf文件的属性索引,其文件名格式为shapefile.columnname.atx(ArcGIS 8及之后的版本).
shp.xml— 以XML格式保存元数据。
.cpg— 用于描述.dbf文件的代码页,指明其使用的字符编码。
了解更多请移步下方链接: 链接: SHAPEFILE.
问题分析
shp文件解析入库Oracle数据库的方式有很多种,例如1,后台Java使用GeoTools解析并入库 2,使用Oracle自带插件解析并入库
这里需要说明的是,两种方式我都有尝试,但是第二种方式并不尽如人意,因为oracle自带的解析shp文件的类年代久远,长时间没有更新,使用的人也并不多,所以使用与学习起来不太理想。
这里就采用Geotools解析shp文件入库的方式来作为此demo的示例。
使用Geotools解析shp文件并入库的方式有两种
1,是通过geotoos解析shp文件后将需要的数据转sql,然后插入到Oracle。
2,是通过geotoos自带插件直接将shp文件写入Oracle,但是由于此方法并不是很好支持oracle直接写入,所以目前采用第一种。
关于geotools的使用介绍的详细内容,大家可以移步官网,后期我会出一些最想的教程,包括各种接口,类,方法的详细使用方法。
链接: lGeoTools.
事先准备,引入依赖:
此demo是采用Idea创建的maven QuickStart 项目
<!-- Geotools版本控制-->
<properties>
<java.version>1.8</java.version>
<geotools.version>24-SNAPSHOT</geotools.version>
</properties>
<!-- Geotools相关-->
<repositories>
<repository>
<id>osgeo</id>
<name>OSGeo Release Repository</name>
<url>https://repo.osgeo.org/repository/release/</url>
<snapshots><enabled>false</enabled></snapshots>
<releases><enabled>true</enabled></releases>
</repository>
<repository>
<id>osgeo-snapshot</id>
<name>OSGeo Snapshot Repository</name>
<url>https://repo.osgeo.org/repository/snapshot/</url>
<snapshots><enabled>true</enabled></snapshots>
<releases><enabled>false</enabled></releases>
</repository>
</repositories>
<dependencies>
<!--geotools-->
<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>
</dependencies>
代码实现,(shp文件转sql):
Demo类
package com.lyf.oracle.shape;
import com.lyf.oracle.modo.Fields;
import com.lyf.oracle.untils.ShpUntils;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Author Unen_
* @Date 2020-06 10:25
*/
public class Demo{
/**注意以下类导包千万不要导错了,可以看看我上面的包*/
//用于存储读取到的shp数据源
private SimpleFeatureSource featureSource = null;
//用于存储表明 因为表明多次用到
private String tableName = "";
//用于存取读取的shp文件属性信息
private List<Fields> fields = new ArrayList<>();
//读取shp文件,需要一个文件路径, 此方法执行完后,SimpleFeatureSource已获取数据源信息
public void readShape(String shpfile){
try {
File file = new File(shpfile);
ShapefileDataStore shpDataStore = null;
shpDataStore = new ShapefileDataStore(file.toURL());
//设置编码 防止中文属性乱码
Charset charset = Charset.forName("GBK");
shpDataStore.setCharset(charset);
//这里getTypeNames()读取的是多个数据表的名称,默认第一个为表名
tableName = shpDataStore.getTypeNames()[0];
featureSource = shpDataStore.getFeatureSource (tableName);
}catch (Exception e){
e.printStackTrace();
}
}
//获取shp文件的属性信息
public void getShpFields(){
//通过featureSource 获取具体的元素信息
SimpleFeatureType schema = featureSource.getSchema();
List<AttributeDescriptor> attrs= schema.getAttributeDescriptors();
for(int i=0;i<attrs.size();i++){
AttributeDescriptor attr = attrs.get(i);
//获取属性类型 ,会返回各种类型
Class<?> cls = attr.getType().getBinding();
//获取属性最大长度 ,这里的ShpUntils.GetLength()是我定义的一个工具类,下方会贴出,用于创建表时字段长度限制
String length = ShpUntils.GetLength(attr.getType().getRestrictions());
//获取属性名称
String clsName = cls.getName();
//关怀这个方法的作用,你输出打印前后的结果就知道了
clsName = clsName.substring(clsName.lastIndexOf(".")+1).toLowerCase();
//利用构造函数,注入值,因为类型不同所以导致字段长度不同,所以要进行判断
if (length==null||"".equals(length)){
Fields field = new Fields(attr.getLocalName(), clsName,0);
fields.add(field);
}else{
Fields field = new Fields(attr.getLocalName(), clsName,Integer.parseInt(length));
fields.add(field);
}
}
}
//创建tablesql ,这里是如oracle库,语法结构与mysql数据库一样
public String createTableSql(){
StringBuffer sql = new StringBuffer();
sql.append("CREATE TABLE "+tableName+" (");
for(int i=0, size = fields.size();i<size;i++){
Fields field = fields.get(i);
//Oracle里面名称都是用大写
String filedname = field.getFieldname().toUpperCase();
String fieldtype = field.getFieldtype();
Integer length=field.getMaxLength();
String judgment = ShpUntils.typeJudgment(fieldtype);
//进行字段长度拼接
if (length>0){
sql.append(filedname+" "+judgment+"("+length+")");
}else {
sql.append(filedname+" "+judgment);
}
//去除最后一个逗号
if(i!=size-1) {
sql.append(", ");
}
}
String reuslt=sql.append(");").toString();
return reuslt ;
}
// 插入sql
public String insertValueSql() {
try {
SimpleFeatureCollection result = featureSource.getFeatures();
SimpleFeatureIterator itertor = result.features();
StringBuffer sql = new StringBuffer();
while (itertor.hasNext()){
SimpleFeature feature = itertor.next();
StringBuffer _sql = new StringBuffer();
_sql.append("insert into "+tableName+" values(");
for(int i=0, size = fields.size();i<size;i++){
Fields field = fields.get(i);
String filedname = field.getFieldname();
//这里根据字段名称判断类型 因为下划线不好识别,所以将the——geom替换一下
filedname = filedname=="the_geom"?"shapefile":filedname;
String fieldtype = field.getFieldtype();
if(filedname!="shapefile"){
if(fieldtype.equals("string")){
_sql.append("'"+feature.getAttribute(filedname)+"'");
}else {
_sql.append(feature.getAttribute(filedname));
}
}else{
//这里很关键,决定了sql能否插入数据库的主要原因,将wkt转几何对象,具体原因请自行百度,后面也会写博客做相关介绍!!!
Geometry geom = (Geometry)feature.getAttribute("the_geom");
_sql.append("sdo_geometry('"+geom.toString()+"', 4326)");
}
if(i!=size-1){
_sql.append(", ");
};
}
_sql.append(");\r\n");
sql.append(_sql);
}
return sql.toString();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
//方法测试与调用
public static void main(String[] args) {
Demo s = new Demo();
//读取
s.readShape("C:/Users/Unen_/Desktop/GisWork/T_SHUIWEI.shp");
//获取字段名称 类型 长度
s.getShpFields();
//返回创建表sql
String sql = s.createTableSql();
System.out.println(sql);
//返回插入表sql
String sql1 = s.insertValueSql();
System.out.println(sql1 );
}
}
Demo类
package com.lyf.oracle.modo;
import java.io.Serializable;
/**
* @Author Unen_
* @Date 2020-08 10:34
*/
public class Fields implements Serializable {
/**
* 字段名称
*/
private String Fieldname = "";
/**
* 字段类型
*/
private String Fieldtype = null;
/**
* 字段长度
*/
private Integer MaxLength = null;
public Fields(String fieldname, String fieldtype ,Integer maxLength ) {
MaxLength = maxLength;
Fieldname = fieldname;
Fieldtype = fieldtype;
}
public String getFieldname() {
return Fieldname;
}
public void setFieldname(String fieldmame) {
Fieldname = fieldmame;
}
public String getFieldtype() {
return Fieldtype;
}
public void setFieldtype(String fieldtype) {
Fieldtype = fieldtype;
}
public Integer getMaxLength() {
return MaxLength;
}
public void setMaxLength(Integer maxLength) {
MaxLength = maxLength;
}
}
ShpUntils 类
package com.lyf.oracle.untils
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
/**
* @Author Unen_
* @Date 2020-08 11:25
*/
public class ShpUntils {
/**
*传入 filer对象,解析返回长度字符串
* @param o
* @return
*/
public static String GetLength(Object o){
String string = o.toString();
List<String> lis = Arrays.asList(string.split(""));
String current="";
for (int i = 0; i < lis.size(); i++) {
String s = lis.get(i);
boolean integer = isInteger(s);
if (integer){
current+=s;
}
}
return current;
}
/**
* 过滤1~9的数字
* @param str
* @return
*/
public static boolean isInteger(String str) {
String rgex="[0-9]*";
Pattern pattern = Pattern.compile(rgex);
return pattern.matcher(str).matches();
}
/** 功能描述: Java类型与数据库类型转换
* @param
* @return:
* @Author: Unen_
* @Date: 2020/6/4 15:04
*/
public static String typeJudgment(String name){
if (name.equals("point")||name.equals("multiPolygon")||name.equals("multiLineString")){
return "\"MDSYS\""+"."+"\"SDO_GEOMETRY\"" ;
}else if(name.equals("integer")||name.equals("long")){
return "NUMBER" ;
}else if(name.equals("double")){
return "FLOAT" ;
}else if(name.equals("string")){
return "VARCHAR2" ;
}else if(name.equals("date")){
return "TIMESTAMP" ;
}
return null;
}
}
结尾
其实因为着急实现功能,所以很多东西没写全,比如Oracle空间元数据的添加,空间索引的建立,没有做很好的封装,也有很多值得优化的空间,写这个博客只为记录自己当下解决问题的思路。
最后
关于最后入库的问题,我会在下篇博文种贴出,以及优化后的代码我也会贴出。886