2

I want to export a table in oracle containing a spatial field as a shp file. This error occurs when I run the export program.I think the length of the field in the table saved to the shp file causes this problem. The longest length of the field in the table is NVARCHAR2(200).But the strange thing is that I manually set the field length information saved to the shp file in the program, but it seems to be inoperative.

here's important code:

public static void doShp(String table) {
    JDBCDataStore jds = null;
    Connection conn = null;
    Transaction tran = new DefaultTransaction();
    String newTable = null;
    try {
        //获取连接
        jds = connOra();
        conn = jds.getConnection(tran);
        newTable = createTempTableOrNotByConfig(table, conn);
        //根据表名获取所有列的结果集
        ResultSet rs = conn.getMetaData().getColumns(null, null, newTable,
                null);
        List<Map<String, Object>> metalist = new ArrayList<Map<String, Object>>();
        //循环将列信息放入List<map>中
        while (rs.next()) {
            String columnName = rs.getString("COLUMN_NAME"); // 列名
            String dataTypeName = rs.getString("TYPE_NAME"); // java.sql.Types类型名称(列类型名称)
            int columnSize = rs.getInt("COLUMN_SIZE"); // 列大小
            int decimalDigits = rs.getInt("DECIMAL_DIGITS"); // 小数位数
            String isNullAble = rs.getString("IS_NULLABLE");
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("columnName", columnName);
            map.put("dataTypeName", dataTypeName);
            map.put("columnSize", columnSize);
            map.put("decimalDigits", decimalDigits);
            map.put("isNullAble", isNullAble);
            metalist.add(map);
        }
        rs.close();
        //根据表名获取主键信息
        ResultSet res1 = conn.getMetaData()
                .getPrimaryKeys(null, null, newTable);
        String columnName = "";
        while (res1.next()) {
            columnName = res1.getString("COLUMN_NAME");
        }
        res1.close();
        Query q = new Query();
        q.setTypeName(newTable);
        //如果有导出的条件,则拼接
        if (cmap.get("filter") != null && !cmap.get("filter").isEmpty()) {
            q.setFilter(CQL.toFilter(cmap.get("filter")));
        }
        // SortBy sb=new SortByImpl("",SortOrder.ASCENDING);
        q.setSortBy(new SortBy[]{SortBy.NATURAL_ORDER});
        //根据query对象获取FeatureReader对象,读取oracle数据,sfs是每行数据的集合体
        FeatureReader<SimpleFeatureType, SimpleFeature> sfs = jds
                .getFeatureReader(q, null);
        //输出文件对象
        File newFile = new File(BASE_PATH + "/map/output/" + newTable + ".shp");
        Map<String, Serializable> params = new HashMap<String, Serializable>();
        params.put("url", (Serializable) newFile.toURI().toURL());
        params.put("create spatial index", (Serializable) Boolean.TRUE);
        ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();

        //根据params参数获取ShapefileDataStore数据源对象
        ShapefileDataStore dataStore = (ShapefileDataStore) factory
                .createNewDataStore(params);
        //columnName-主键 metalist-列相关信息 table-表名
        SimpleFeatureType type = getType(columnName, metalist, newTable);
        //设置列相关信息-----------------------
        dataStore.createSchema(type);
        //设置编码
        dataStore.setCharset(Charset.forName(cmap.get("charset")));
        //设置事务
        Transaction transaction = new DefaultTransaction("Reproject");
        //获取指定typename和transaction的writer对象(typename为表名)
        String typeName = type.getTypeName();
        FeatureWriter<SimpleFeatureType, SimpleFeature> writer = dataStore.getFeatureWriterAppend(typeName, transaction);
        //获取表中的属性->集合
        List<AttributeDescriptor> list = type.getAttributeDescriptors();
        //获取空间信息字段名
        String the_geom = getGeomCol(newTable, conn);
        // 创建一个事务
        int i = 0;
        try {
            //如果读取不到了就跳出循环
            while (sfs.hasNext()) {
                SimpleFeature feature = sfs.next();


                SimpleFeature copy = writer.next();
                //表中的属性->集合
                for (int j = 0; j < list.size(); j++) {
                    AttributeDescriptor dr = list.get(j);
                    //如果不是空间信息字段
                    if (!dr.getLocalName().equals(the_geom)) {
                        if (isContainChinese(dr.getLocalName())) {
                            //包含中文
                            copy.setAttribute(j, feature.getAttribute(j));
                        } else {
                            copy.setAttribute(dr.getName(), feature.getAttribute(dr.getName()));
                        }
                    }
                }
                Geometry geometry = (Geometry) feature.getDefaultGeometry();
                copy.setDefaultGeometry(geometry);
                writer.write();
                i++;
            }
            tran.commit();
            transaction.commit();
            log.info("导出shp文件成功,共导出数据" + i + "条,请在" + BASE_PATH
                    + "/output/目录下查看结果");
        } catch (Exception problem) {
            log.error(problem);
            transaction.rollback();
            tran.rollback();
            problem.printStackTrace();
        } finally {
            jds.dispose();
            writer.close();
        }
    } catch (Exception e) {
        log.error(e);
        e.printStackTrace();
    } finally {
        if (jds != null)
            jds.dispose();
    }
    updateOutFileName(table.toUpperCase());
    saveTempTableNameToConf(newTable);
}


private static SimpleFeatureType getType(String pkcol, List<Map<String, Object>> list, String newTable) {
    //定义图形信息和属性信息
    SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
    //设置表名
    builder.setName(newTable);
    //根据配置设置二维坐标参考系统
    try {
        crs = CRS.decode("EPSG:" + cmap.get("epsg"));
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    builder.setCRS(crs);
    //循环设置列的列名及其类型(不设置主键)
    for (Map<String, Object> m : list) {
        if (m.get("columnName").toString().equals(pkcol)) {
            continue;
        }
        //获取类型为空间属性类型的列
        if (m.get("dataTypeName").equals("SDO_GEOMETRY")) {
            builder.add(m.get("columnName").toString(), Polygon.class);
        }
        if (m.get("dataTypeName").toString().equals("NUMBER")) {
            //decimalDigits-小数位数
            if (Integer.parseInt(m.get("decimalDigits").toString()) > 0) {
                builder.add(m.get("columnName").toString(), Double.class);
            } else {
                builder.add(m.get("columnName").toString(), Integer.class);
            }

        }
        if (m.get("dataTypeName").toString().equals("FLOAT") || m.get("dataTypeName").toString().equals("DOUBLE")) {
            builder.add(m.get("columnName").toString(), Double.class);
        }
        if (m.get("dataTypeName").toString().equals("VARCHAR2") || m.get("dataTypeName").toString().equals("NVARCHAR2")) {
            builder.length(2 * (Integer.parseInt(m.get("columnSize").toString())))
                    .add(m.get("columnName").toString(), String.class);
        }
        if (m.get("dataTypeName").toString().equals("DATE")) {
            builder.add(m.get("columnName").toString(), Date.class);

        }
    }
    return builder.buildFeatureType();
}

Error:

java.io.IOException: Current fid index is null, next must be called before write() at org.geotools.data.shapefile.fid.IndexedFidWriter.write(IndexedFidWriter.java:243) at org.geotools.data.shapefile.IndexedShapefileFeatureWriter.write(IndexedShapefileFeatureWriter.java:97) at org.geotools.data.shapefile.ShapefileFeatureWriter.close(ShapefileFeatureWriter.java:244) at org.geotools.data.shapefile.IndexedShapefileFeatureWriter.close(IndexedShapefileFeatureWriter.java:103) at org.geotools.data.InProcessLockingManager$1.close(InProcessLockingManager.java:316) at org.geotools.data.store.DiffTransactionState.commit(DiffTransactionState.java:196) at org.geotools.data.DefaultTransaction.commit(DefaultTransaction.java:166) at com.bitservice.map.Ora2shpFile.doShp(Ora2shpFile.java:269) at com.bitservice.map.Ora2shpFile.main(Ora2shpFile.java:140) Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: 253 at java.lang.StringBuffer.charAt(StringBuffer.java:202) at org.geotools.data.shapefile.dbf.DbaseFileWriter$FieldFormatter.getFieldString(DbaseFileWriter.java:361) at org.geotools.data.shapefile.dbf.DbaseFileWriter.fieldBytes(DbaseFileWriter.java:225) at org.geotools.data.shapefile.dbf.DbaseFileWriter.write(DbaseFileWriter.java:196) at org.geotools.data.shapefile.ShapefileFeatureWriter.write(ShapefileFeatureWriter.java:439) at org.geotools.data.shapefile.IndexedShapefileFeatureWriter.write(IndexedShapefileFeatureWriter.java:98) at org.geotools.data.InProcessLockingManager$1.write(InProcessLockingManager.java:304) at org.geotools.data.store.DiffTransactionState.commit(DiffTransactionState.java:180) ... 3 more

here's the type infomation:

FID INTEGER
THE_GEOM    MDSYS.SDO_GEOMETRY
BSM NUMBER(10)
YSDM    VARCHAR2(10)
ZDDM    VARCHAR2(19)
BDCDYH  VARCHAR2(28)
ZDTZM   VARCHAR2(2)
ZL  VARCHAR2(200)
ZDMJ    NUMBER(15,4)
MJDW    VARCHAR2(2)
YT  VARCHAR2(4)
DJ  VARCHAR2(2)
JG  NUMBER(15,4)
QLLX    VARCHAR2(2)
QLXZ    VARCHAR2(4)
QLSDFS  VARCHAR2(2)
RJL VARCHAR2(200)
JZMD    NUMBER(3,2)
JZXG    NUMBER(5,2)
ZDSZD   NVARCHAR2(200)
ZDSZN   VARCHAR2(200)
ZDSZX   VARCHAR2(200)
ZDSZB   VARCHAR2(200)
ZDT VARCHAR2(200)
TFH VARCHAR2(50)
BZ  VARCHAR2(200)
YTMC    VARCHAR2(200)
QXDM    VARCHAR2(6)
Mr.H
  • 59
  • 7
  • This just means that the subscript of a string exceeds 253,No specific code found – Mr.H Sep 21 '18 at 09:29
  • I am afraid not, the length of each field is specified.I measured it in the code, and the longest read field is only 146. – Mr.H Sep 21 '18 at 09:33
  • I queried the length of the data in the database, the field type of the query belongs to vachar2 or nvachar2,Confirmed that the longest data length is 146 – Mr.H Sep 21 '18 at 09:41
  • Can you print out `type` and add it to the question - it looks like one of the columns is too wide for the shapefile spec. – Ian Turton Sep 22 '18 at 09:37
  • I have added the information to the description, and trouble you to see if there is any problem. – Mr.H Sep 25 '18 at 01:42
  • I got the same error when I accidently tried to add empty `Point` objects (`Point p = geometryFactory.createPoint();`) to a `SimpleFeatureBuilder`. – Neph Sep 04 '19 at 09:32

2 Answers2

1

i noticed the same error these days while extracting some points from an ´shp´ to a new ´shp´. the new file was ´UTF-8´ encoded, as i always create files using this charset.

dataStore.setCharset(Charset.forName("UTF-8"));

after some investigation i noticed that the original file was in different charset. so i tried removing the setCharset and the Exception is gone.

i guess you have a similar problem with an charset.

lumo
  • 790
  • 1
  • 8
  • 23
  • I tried it, but it didn't work.the question is the length of the column value is too long – Mr.H Oct 10 '18 at 03:09
  • In my case the length of the column is 254 and the vakue was 253 so all finde. Still got the error. Even with replacing the values with test values failed... – lumo Oct 10 '18 at 04:28
0

I can't see a problem in your code but I suspect that you have Chinese characters that are 2 bytes wide in your data which would put any attribute with a width of 128 chars over the (stupid) width limit that shapefile enforces.

You may be better off using a modern format like geopackage, that GeoTools supports in the same way as shapefile but without the restrictions.

Ian Turton
  • 10,018
  • 1
  • 28
  • 47
  • ok.I finally intercepted the redundant data and printed out the prompt information to solve this problem. – Mr.H Oct 10 '18 at 03:08