解決Java中基于GeoTools的Shapefile讀取亂碼的問題
前言
文件編碼(File Encoding)是指文件在計算機中存儲時所使用的字符編碼方式。字符編碼是將字符(如字母、數(shù)字、標點符號等)轉(zhuǎn)換成計算機可以直接存儲和處理的數(shù)字或二進制代碼的過程。不同的編碼方式?jīng)Q定了文件中字符如何被表示和存儲,以及這些字符如何被不同的軟件或系統(tǒng)正確地讀取和顯示。在進行空間數(shù)據(jù)處理的時候,通常會涉及大量的空間數(shù)據(jù),為了更加詳細且準確的描述這些空間數(shù)據(jù),我們通過會配置一些屬性數(shù)據(jù)。
Shapefile屬性字段的編碼通常指的是存儲在shapefile的dbf(數(shù)據(jù)庫文件)中的屬性數(shù)據(jù)的字符編碼方式。Shapefile是一種用于存儲地理空間數(shù)據(jù)的文件格式,它由多個文件組成,其中dbf文件用于存儲每個幾何形狀的屬性數(shù)據(jù)。

1、Shapefile屬性字段編碼的情況:
默認編碼:
在不同的軟件或庫中,shapefile的默認編碼可能有所不同。例如,ArcGIS Desktop在較新版本(如10.2.1及以后)中,shapefile (.DBF) 的編碼頁的默認設置為UTF-8(UNICODE)。而在一些其他軟件或庫中,如Java GDAL庫,默認可能使用ISO-8859-1編碼,這會導致中文等非西歐字符出現(xiàn)亂碼問題。
編碼設置:
在使用某些軟件或庫處理shapefile時,可以通過設置來改變屬性字段的編碼方式。例如,在Java GDAL庫中,可以通過調(diào)GDAL.SetConfigOption("SHAPE_ENCODING","UTF-8")來設置GDAL庫的默認編碼為UTF-8,從而避免中文屬性亂碼的問題。在ArcGIS中,雖然默認編碼可能是UTF-8,但也可以通過修改注冊表中的dbfDefault值來指定不同的編碼方式。不過,這種方法主要影響ArcGIS Desktop生成的shapefile和dBASE文件的編碼類型,且僅對ArcGIS Desktop生效。
編碼轉(zhuǎn)換:
如果已經(jīng)存在編碼不匹配的shapefile文件,可能需要通過編碼轉(zhuǎn)換工具來修改其屬性字段的編碼方式。例如,可以使用FME Workbench等GIS數(shù)據(jù)轉(zhuǎn)換工具來轉(zhuǎn)換shapefile的編碼。也可以使用編程方式,如利用geotools等庫來讀取原始編碼的shapefile文件,并以新的編碼方式重新寫入數(shù)據(jù),從而實現(xiàn)編碼的轉(zhuǎn)換。
注意事項
在處理shapefile屬性字段編碼時,需要確保整個處理流程中的編碼方式一致,以避免出現(xiàn)亂碼或數(shù)據(jù)丟失等問題。如果shapefile文件是從不同來源獲取的,可能需要先確認其編碼方式,以便在后續(xù)處理中正確讀取和寫入數(shù)據(jù)。在進行編碼轉(zhuǎn)換時,應謹慎操作,以免損壞原始數(shù)據(jù)。建議在轉(zhuǎn)換前備份原始文件,并在轉(zhuǎn)換后進行驗證以確保數(shù)據(jù)的完整性和準確性。
本文主要講述使用Java編程語言進行地理信息數(shù)據(jù)解析的時候,遇到Shapefile的屬性信息亂碼的幾種情況,以及根據(jù)不同的編碼設置來進行屬性信息的解析。博文首先介紹采用不同的字符集編碼的shapefile文件,然后在Qgis中打開屬性表,查看相關的字符展示情況,接著說明在Java當中調(diào)用Geotools時,為經(jīng)過字符編碼處理和經(jīng)過字符編碼處理后的對比,讓大家熟悉在Geotools的開發(fā)過程中,掌握字符編碼的設置。
一、Shp文件常見的字符集編碼
為了講解使用不同的編碼來展示空間數(shù)據(jù),我們首先來介紹基礎的數(shù)據(jù),即三份不同的空間數(shù)據(jù)格式,其格式都是shapefile的。但是在不同的空間記錄中,其字段的值是采用不同的編碼的。在這里,采用QGIS這款軟件來進行屬性數(shù)據(jù)的展示,方便大家了解日常中的數(shù)據(jù)展示。
1、System編碼
第一種要介紹的就是System的編碼方式,這里采用的是用我國的Lake圖層信息,首先我們在Qgis中打開這份數(shù)據(jù)來看一下,文件的本地路徑為:
F:\vector_data\地理數(shù)據(jù)20240912\地理數(shù)據(jù)20240912\水系河流\1 全國1-5級標準河流-wgs84\主要湖泊面文件\Lake.shp
將數(shù)據(jù)在Qgis軟件中打開可以看到其主要的源信息描述如下:

可以在編碼一欄中看到,這份文件的編碼是System的。 為了看到其里面的屬性信息,可以右鍵點擊shp數(shù)據(jù),點擊打開屬性表就可以看到這份數(shù)據(jù)的完整的數(shù)據(jù)信息,打開后相關信息如下所示:

在上圖的紅線框中很明顯可以看到,有一列叫NAME的,它的值是有亂碼的,并沒有是我們常見的編碼。 因此這算是亂碼的第一種情況。
2、ISO-8859-1編碼
第二種也是常見的ISO-8859-1編碼,這里準備的數(shù)據(jù)是一份湖南省的鄉(xiāng)鎮(zhèn)邊界數(shù)據(jù),其在文件磁盤中目錄如下,:
C:\BaiduDownload\湖南省\湖南省_鄉(xiāng)鎮(zhèn)邊界.shp
同樣的,我們使用QGis軟件打開上面的鄉(xiāng)鎮(zhèn)邊界.shp文件,打開后可以看到很明確的字符編碼信息:

同樣的我們使用QGIS來進行屬性數(shù)據(jù)的打開查看, 詳細如下圖所示:

3、UTF-8編碼
這應該算是比較標準的編碼方式,如果進行數(shù)據(jù)制作的時候,都是統(tǒng)一采用UTF-8的模式,那么這種方式無疑是最好的,估計也不存在字符編碼的問題了。

這個時候,在QGIS中打開屬性表,其屬性字段的內(nèi)容是正??梢灾苯宇A覽的,詳情如下圖所示。

當然,字符編碼的處理方式根據(jù)項目的不同,也會有不同的設置,種類繁多,不甚枚舉,這里僅以這幾項為例作為例子來講解,如果以后在開發(fā)過程中,遇到這種情況,可以根據(jù)實際來進行編碼的擴充和修復。
二、GeoTools解析實戰(zhàn)
在上面的一節(jié)中,我們簡單的對三種不同的編碼方式的shapefile文件進行了簡單的介紹,本節(jié)則重點介紹如何使用Java開發(fā)語言,使用GeoTools的開發(fā)組件進行編碼的處理和轉(zhuǎn)換,將屬性數(shù)據(jù)可以成功讀取到我們的應用程序中。在這里需要統(tǒng)一說明的是,在進行數(shù)據(jù)的處理和轉(zhuǎn)換的時候,為了進行數(shù)據(jù)的演示,我們僅將數(shù)據(jù)的前10行數(shù)據(jù)記載處理,這樣如果有亂碼的問題,我們就可以直接進行干預,通過修改其它的字符集函數(shù)的方式來保證文字的識別與處理。
1、未進行字符處理
首先我們來加載UTF-8的矢量數(shù)據(jù),測試一下使用UTF-8的情況下,如何使用GeoTools的方法來進行屬性表格的解析。下面來看如何使用GeoTools來進行空間屬性數(shù)據(jù)的解析與展示,關鍵代碼如下所示:
/**
* * 不做任何處理展示shp文件數(shù)據(jù)詳情
*
* @param shpFile shp文件地址
* @throws Exception
*/
protected static void showShpDetails(String shpFile) throws Exception {
File file = new File(shpFile);
if (!file.exists()) {
System.out.println("文件不存在");
return;
}
ShapefileDataStore store = new ShapefileDataStore(file.toURI().toURL());
String typeName = store.getTypeNames()[0];
// 創(chuàng)建一個Query對象
Query query = new Query(typeName);
// 設置查詢返回的最大特征數(shù)為10
query.setMaxFeatures(10);
SimpleFeatureSource featureSource = store.getFeatureSource();
// 執(zhí)行查詢
SimpleFeatureCollection simpleFeatureCollection = featureSource.getFeatures(query);
SimpleFeatureIterator itertor = simpleFeatureCollection.features();
// 遍歷featurecollection
while (itertor.hasNext()) {
SimpleFeature feature = itertor.next();
Collection<Property> p = feature.getProperties();
Iterator<Property> it = p.iterator();
// 遍歷feature的properties
while (it.hasNext()) {
Property pro = it.next();
if (null != pro && null != pro.getValue()) {
String field = pro.getName().toString();
String value = pro.getValue().toString();
System.out.println(field + "===" + value);
}
}
System.out.println("-----------------------------------------------------");
}
}在上面的代碼中,需要注意的地方就是,我們想要在查詢的時候只查10條,那么就需要使用到GeoTools的查詢Query對象,通過結合Query對象來實現(xiàn)只查10條。10條的設置是個經(jīng)驗值,可以根據(jù)服務器的速度和性能來進行平衡,可以一次處理更多的數(shù)據(jù)。運行測試用例來看其讀取的結果如下:

可以看到,在IDE的控制臺中,讀取出來的空間屬性信息都是亂碼。
2、亂碼問題的解決
要想解決亂碼的問題,首先要找到根源。我們需要對屬性信息字段進行字符集編碼的控制。因此我們在互聯(lián)網(wǎng)上查詢一下,時候有相應的方案。在這里哪怕不管具體的方案,也要了解為什么會出現(xiàn)這個問題。我們來看下ShapefileDataStore這個對象,這個對象是有一個關于字符集的編碼的,如下所示:

如果看過源碼的話,各位小伙伴會發(fā)現(xiàn),在GeoTools中有默認的編碼集,即:Charset charset = DEFAULT_STRING_CHARSET;
public static final Charset DEFAULT_STRING_CHARSET =
(Charset) ShapefileDataStoreFactory.DBFCHARSET.getDefaultValue();其實現(xiàn)的實際邏輯代碼如下:
/**
* Optional - character used to decode strings from the DBF file. If none is provided, the
* factory will instruct {@link ShapefileDataStore} to try to guess a charset from CPG file,
* before using a default value.
*
* @see ShapefileDataStore#setTryCPGFile(boolean)
*/
public static final Param DBFCHARSET =
new Param(
"charset",
Charset.class,
"character used to decode strings from the DBF file",
false,
StandardCharsets.ISO_8859_1,
new KVP(Param.LEVEL, "advanced")) {
/*
* This is an example of a non simple Param type where a custom parse method is required.
*
* @see org.geotools.data.DataStoreFactorySpi.Param#parse(java.lang.String)
*/
@Override
public Object parse(String text) throws IOException {
return Charset.forName(text);
}
@Override
public String text(Object value) {
return ((Charset) value).name();
}
};通過上面的代碼可以看到,這里使用的默認編碼是:StandardCharsets.ISO_8859_1,也就是ISO-8859-1的方式。
3、轉(zhuǎn)碼支持
了解了亂碼的產(chǎn)生原理之后,我們來進行相應的代碼轉(zhuǎn)換,關于編碼的轉(zhuǎn)換有兩種方式,第一種統(tǒng)一在Store一層就進行轉(zhuǎn)碼。這樣比較單一,也比較簡單。第二種就是在每一個value中進行編程式轉(zhuǎn)碼,這樣不僅麻煩,而且效率低。為了支持全局處理編碼等,我們將函數(shù)進行已統(tǒng)一的封裝,增加了自定義編碼的支持:
/**
* *展示shp文件數(shù)據(jù)詳情
*
* @param shpFile shp文件地址
* @param unifySetting 是否統(tǒng)一設置字符
* @param chartSet 需要設置的字符編碼
* @throws Exception
*/
protected static void showShpDetails(String shpFile, boolean unifySetting, String chartSet) throws Exception {
File file = new File(shpFile);
if (!file.exists()) {
System.out.println("文件不存在");
return;
}
ShapefileDataStore store = new ShapefileDataStore(file.toURI().toURL());
String typeName = store.getTypeNames()[0];
// 創(chuàng)建一個Query對象
Query query = new Query(typeName);
// 設置查詢返回的最大特征數(shù)為10
query.setMaxFeatures(10);
if (unifySetting) {
store.setCharset(Charset.forName(chartSet));// 設置中文字符編碼
}
SimpleFeatureSource featureSource = store.getFeatureSource();
System.out.println(featureSource);
// 執(zhí)行查詢
SimpleFeatureCollection simpleFeatureCollection = featureSource.getFeatures(query);
SimpleFeatureIterator itertor = simpleFeatureCollection.features();
// 遍歷featurecollection
while (itertor.hasNext()) {
SimpleFeature feature = itertor.next();
Collection<Property> p = feature.getProperties();
Iterator<Property> it = p.iterator();
// 遍歷feature的properties
while (it.hasNext()) {
Property pro = it.next();
if (null != pro && null != pro.getValue()) {
String field = pro.getName().toString();
String value = pro.getValue().toString();
if (!unifySetting) {
// byte[]bytes= value.getBytes("iso8859-1");
byte[] bytes = value.getBytes();
value = new String(bytes, chartSet);
}
System.out.println(field + "===" + value);
}
}
System.out.println("-------------------------------------------------------------");
}
}4、屬性字段編碼結果
下面對集中情況的屬性數(shù)據(jù)進行解析,將輸出的成果在編輯器的控制臺進行綜合展示。

可以在控制臺中看到以下的數(shù)據(jù)都是正常的,
org.geotools.data.shapefile.ShapefileFeatureStore@1b6e1eff the_geom===POINT (113.24947489555838 28.625229546432124) 名稱===南門橋(公交站) 大類===交通設施服務 中類===公交車站 小類===公交車站相關 地址===星通2路 省===湖南省 市===長沙市 區(qū)===長沙縣 WGS84_經(jīng)===113.249474896 WGS84_緯===28.6252295464
同理,其它的數(shù)據(jù)如湖南省鄉(xiāng)鎮(zhèn)邊界數(shù)據(jù),我們使用編碼后來查看具體的輸出。

同樣的,在控制臺中可以看到以下的輸出,
gml_id===layer_township_pg.15847
Name===星子鎮(zhèn)
layer===鄉(xiāng)鎮(zhèn)
code===441882101000
grade===4
----------------------------------------------------------------------
gml_id===layer_township_pg.15849
Name===三水瑤族鄉(xiāng)
layer===鄉(xiāng)鎮(zhèn)
code===441882201000
grade===4
同樣的,編碼方式是ISO-8859-1的數(shù)據(jù)經(jīng)過編碼后正常顯示。 到此,使用Java語言進行GeoTools解析Shp文件的屬性信息時亂碼的問題得到解決。
三、總結
以上就是本文的主要內(nèi)容,本文主要講述使用Java編程語言進行地理信息數(shù)據(jù)解析的時候,遇到Shapefile的屬性信息亂碼的幾種情況,以及根據(jù)不同的編碼設置來進行屬性信息的解析。博文首先介紹采用不同的字符集編碼的shapefile文件,然后在Qgis中打開屬性表,查看相關的字符展示情況,接著說明在Java當中調(diào)用Geotools時,為經(jīng)過字符編碼處理和經(jīng)過字符編碼處理后的對比,讓大家熟悉在Geotools的開發(fā)過程中,掌握字符編碼的設置。行文倉促,定有許多不足之處,如有不當之處,還懇請各位專家和博主在評論區(qū)留言支持,不勝感激。
博文在寫作過程中,參考以下,在此表示表示:
2、java gdal 創(chuàng)建shapefile屬性中文亂碼。
到此這篇關于在Java中基于GeoTools的Shapefile讀取亂碼的問題解決辦法的文章就介紹到這了,更多相關Java GeoTools的Shapefile讀取亂碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
Java并發(fā)J.U.C并發(fā)容器類list set queue
SpringBoot中web模版數(shù)據(jù)渲染展示的案例詳解
java文件復制代碼片斷(java實現(xiàn)文件拷貝)
SpringCloud項目中Feign組件添加請求頭所遇到的坑及解決

