目录
关于ShapeFile
可以参考百度百科https://baike.baidu.com/item/shapefile%E6%96%87%E4%BB%B6/11041662?fr=aladdin
这里需要说的就是本文档生成的ShapeFile包含.shp、.shx、.dbf、.prj四类文件
创建ShapeFile的逻辑
根据GDAL3.2的API文档可知,创建ShapeFile的过程为:
1、创建Esri ShapeFile驱动类。
2、创建数据源类;
3、在数据源类上创建图层类;
4、在图层类上创建字段类、要素类;
5、保存数据源。
这里附一个gdal.jar的maven标签
<dependency>
<groupId>org.gdal</groupId>
<artifactId>gdal</artifactId>
<version>3.3.0</version>
<type>pom</type>
</dependency>
gdal是用C++编写的,可执行文件下载https://www.gisinternals.com/,里面也包含gdal的jar包,通过gdalalljni.dll调用底层的代码。
创建实体类
为了简化工具类的代码,这里先创建实体类:
字段实体类
package com.util.entity;
public class ShapeTitle {
/**
* 字段名称
*/
private String name;
/**
* 字段长度
*/
private int length;
/**
* 字段类型
*/
private int type;
public ShapeTitle(String name, int type) {
this.name = name;
this.type = type;
}
public ShapeTitle(String name, int length, int type) {
this.name = name;
this.length = length;
this.type = type;
}
public ShapeTitle() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
ShapeFile实体类
package com.util.entity;
import java.util.List;
import java.util.Map;
import org.gdal.osr.SpatialReference;
public class ShapeFileBean {
/**
* 空间参考
*/
private SpatialReference Sptref;
/**
* 字段实体类
*/
private List<ShapeTitle> title;
/**
* 矢量图层的要素类
*/
private List<Map<Integer,Object>> feature;
public ShapeFileBean() {
}
public List<ShapeTitle> getTitle() {
return title;
}
public void setTitle(List<ShapeTitle> title) {
this.title = title;
}
public List<Map<Integer,Object>> getFeature() {
return feature;
}
public void setFeature(List<Map<Integer,Object>> feature) {
this.feature = feature;
}
public SpatialReference getSptref() {
return Sptref;
}
public void setSptref(SpatialReference sptref) {
Sptref = sptref;
}
}
工具类
package com.util;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
import org.gdal.gdal.gdal;
import org.gdal.ogr.DataSource;
import org.gdal.ogr.Driver;
import org.gdal.ogr.Feature;
import org.gdal.ogr.FeatureDefn;
import org.gdal.ogr.FieldDefn;
import org.gdal.ogr.Geometry;
import org.gdal.ogr.Layer;
import org.gdal.ogr.ogr;
import com.util.entity.ShapeFileBean;
import com.util.entity.ShapeTitle;
public class ShapeFileWriter {
/**
* 文件名称
*/
private String FileName;
/**
* 驱动名称
*/
private String strDriverName = "ESRI Shapefile";
/**
* 驱动
*/
private Driver oDriver;
/**
* 图层
*/
private Layer oLayer;
/**
* 数据源
*/
private DataSource oDS;
/**
* ShapeFile实体类
*/
private ShapeFileBean shpbean;
static {
// 注册
ogr.RegisterAll();
// 为了支持中文路径
gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
}
public ShapeFileWriter(String filename, ShapeFileBean shpbean) {
this.FileName = filename;
this.shpbean = shpbean;
}
/**
* 创建ShapeFile
*/
public void create() {
createDriver();
createDataSource();
createoLayer();
if (oLayer!= null) {
createallField();
createFeature();
save();
}
}
/**
* 创建Driver
*/
private void createDriver() {
oDriver = ogr.GetDriverByName(strDriverName);
if (oDriver == null) {
LogUtil.debug(FileName + " 驱动不可用!");
return;
}
}
/**
* 创建数据源
*/
private void createDataSource() {
oDS = oDriver.CreateDataSource(FileName, null);
if (oDS == null) {
LogUtil.debug("创建矢量文件【" + FileName + "】失败!");
return;
}
}
/**
* 创建图层
*/
private void createoLayer() {
Vector<String> options = new Vector<>();
options.add("ENCODING=UTF-8");
File file = new File(FileName);
String name = file.getName();
name = name.substring(0, name.lastIndexOf("."));
oLayer = oDS.CreateLayer(name, shpbean.getSptref(), ogr.wkbPoint, options);
if (oLayer == null) {
LogUtil.debug("图层创建失败!");
return;
}
}
/**
* 从ShapeTitle类读取数据,批量创建字段
*/
private void createallField() {
List<ShapeTitle> list = shpbean.getTitle();
for (ShapeTitle title:list) {
if((title.getLength()+"").isEmpty()) {
createField(title.getName(),title.getType());
}else if(!(title.getLength()+"").isEmpty()) {
createField(title.getName(),title.getLength(),title.getType());
}
}
}
/**创建字段(不指定长度)
* @param name
* @param type
*/
private void createField(String name, int type) {
FieldDefn oFieldID = new FieldDefn(name, type);
oLayer.CreateField(oFieldID);
}
/**创建字段(指定长度)
* @param name
* @param length
* @param type
*/
private void createField(String name, int length, int type) {
FieldDefn oFieldName = new FieldDefn(name, type);
oFieldName.SetWidth(length);
oLayer.CreateField(oFieldName);
}
/**
* 创建要素,要素包含属性与地理信息
*/
private void createFeature() {
FeatureDefn oDefn = oLayer.GetLayerDefn();
List<Map<Integer, Object>> feature = shpbean.getFeature();
LogUtil.info(feature.size());
for (Map<Integer, Object> data : feature) {
Feature f = new Feature(oDefn);
for (Entry<Integer, Object> entry : data.entrySet()) {
if (entry.getKey() == -1) {
f.SetGeometry((Geometry) entry.getValue());
} else if (entry.getKey() > -1) {
f.SetField(entry.getKey(), entry.getValue().toString());
}
}
oLayer.CreateFeature(f);
}
}
/**
* 保存数据
*/
private void save() {
if (oDS != null) {
oDS.SyncToDisk();
}
}
}
调用例子
读取Excel表格(含XY坐标)中的属性,生成点状矢量文件
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.gdal.ogr.Geometry;
import org.gdal.ogr.ogr;
import org.gdal.osr.SpatialReference;
import com.hjzx.util.ExcelParser;
import com.hjzx.util.entity.ExcelBean;
import com.util.ShapeFileWriter;
import com.util.entity.ShapeFileBean;
import com.util.entity.ShapeTitle;
public class Excel2ShpConverter {
public static void main(String[] args) {
File file = new File(args[0]);
File dstfile = new File(args[1]);
if (!file.exists()) {
System.out.println("源文件不存在");
return;
}
ShapeFileBean shpbean = new ShapeFileBean();
// 用wkt字符串设置投影
String wktstr = "GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199433]]";
SpatialReference Sptref = new SpatialReference(wktstr);
shpbean.setSptref(Sptref);
// 设置字段
List<ShapeTitle> titilelist = new ArrayList<>();
ShapeTitle id = new ShapeTitle("id", ogr.OFTInteger64);
ShapeTitle zh = new ShapeTitle("站号", 10, ogr.OFTString);
titilelist.add(id);
titilelist.add(zh);
shpbean.setTitle(titilelist);
// 从Excel表中读取属性及经纬度坐标为List
List<Map<Integer, Object>> excelbean = GetDataExcelBean(file.getAbsolutePath());
// 设置要素
List<Map<Integer, Object>> features = new ArrayList<>();
for (Map<Integer, Object> map : excelbean) {
Map<Integer, Object> feature = new LinkedHashMap<>();
// 用wkt字符串创建Geometry
Geometry geo = Geometry.CreateFromWkt("POINT(" + map.get(4) + " " + map.get(5) + ")");
// 将Geometry放到key=-1的位置
feature.put(-1, geo);
feature.put(0, map.get(1));
feature.put(1, map.get(2));
features.add(feature);
}
shpbean.setFeature(features);
ShapeFileWriter shpw = new ShapeFileWriter(dstfile.getAbsolutePath() + file.getName() + ".shp", shpbean);
shpw.create();
}
private static List<Map<Integer, Object>> GetDataExcelBean(String path) {
ExcelParser expaser = new ExcelParser();
expaser.Read(path, 2);
ExcelBean exbean = expaser.GetExcelBean();
List<Map<Integer, Object>> rows = exbean.getRow();
return rows;
}
}
以上代码中GetDataExcelBean是读取表格转换为List<Map<Integer, Object>>的方法,其实现细节与本文主要内容无关,不在这里赘述。
wkt字符串是描述几何对象的文本,其规范可以参考以下网址https://www.osgeo.cn/doc_ogcstd/ogc_standard/ch02_chapter1/chapter.html#wkt
由于水平有限,代码写得不算好,勉强能用。