解決Java中基于GeoTools的Shapefile讀取亂碼的問題
前言
文件編碼(File Encoding)是指文件在計(jì)算機(jī)中存儲(chǔ)時(shí)所使用的字符編碼方式。字符編碼是將字符(如字母、數(shù)字、標(biāo)點(diǎn)符號(hào)等)轉(zhuǎn)換成計(jì)算機(jī)可以直接存儲(chǔ)和處理的數(shù)字或二進(jìn)制代碼的過程。不同的編碼方式?jīng)Q定了文件中字符如何被表示和存儲(chǔ),以及這些字符如何被不同的軟件或系統(tǒng)正確地讀取和顯示。在進(jìn)行空間數(shù)據(jù)處理的時(shí)候,通常會(huì)涉及大量的空間數(shù)據(jù),為了更加詳細(xì)且準(zhǔn)確的描述這些空間數(shù)據(jù),我們通過會(huì)配置一些屬性數(shù)據(jù)。
Shapefile屬性字段的編碼通常指的是存儲(chǔ)在shapefile的dbf(數(shù)據(jù)庫(kù)文件)中的屬性數(shù)據(jù)的字符編碼方式。Shapefile是一種用于存儲(chǔ)地理空間數(shù)據(jù)的文件格式,它由多個(gè)文件組成,其中dbf文件用于存儲(chǔ)每個(gè)幾何形狀的屬性數(shù)據(jù)。
1、Shapefile屬性字段編碼的情況:
默認(rèn)編碼:
在不同的軟件或庫(kù)中,shapefile的默認(rèn)編碼可能有所不同。例如,ArcGIS Desktop在較新版本(如10.2.1及以后)中,shapefile (.DBF) 的編碼頁(yè)的默認(rèn)設(shè)置為UTF-8(UNICODE)。而在一些其他軟件或庫(kù)中,如Java GDAL庫(kù),默認(rèn)可能使用ISO-8859-1編碼,這會(huì)導(dǎo)致中文等非西歐字符出現(xiàn)亂碼問題。
編碼設(shè)置:
在使用某些軟件或庫(kù)處理shapefile時(shí),可以通過設(shè)置來改變屬性字段的編碼方式。例如,在Java GDAL庫(kù)中,可以通過調(diào)GDAL.SetConfigOption("SHAPE_ENCODING","UTF-8")來設(shè)置GDAL庫(kù)的默認(rèn)編碼為UTF-8,從而避免中文屬性亂碼的問題。在ArcGIS中,雖然默認(rèn)編碼可能是UTF-8,但也可以通過修改注冊(cè)表中的dbfDefault值來指定不同的編碼方式。不過,這種方法主要影響ArcGIS Desktop生成的shapefile和dBASE文件的編碼類型,且僅對(duì)ArcGIS Desktop生效。
編碼轉(zhuǎn)換:
如果已經(jīng)存在編碼不匹配的shapefile文件,可能需要通過編碼轉(zhuǎn)換工具來修改其屬性字段的編碼方式。例如,可以使用FME Workbench等GIS數(shù)據(jù)轉(zhuǎn)換工具來轉(zhuǎn)換shapefile的編碼。也可以使用編程方式,如利用geotools等庫(kù)來讀取原始編碼的shapefile文件,并以新的編碼方式重新寫入數(shù)據(jù),從而實(shí)現(xiàn)編碼的轉(zhuǎn)換。
注意事項(xiàng)
在處理shapefile屬性字段編碼時(shí),需要確保整個(gè)處理流程中的編碼方式一致,以避免出現(xiàn)亂碼或數(shù)據(jù)丟失等問題。如果shapefile文件是從不同來源獲取的,可能需要先確認(rèn)其編碼方式,以便在后續(xù)處理中正確讀取和寫入數(shù)據(jù)。在進(jìn)行編碼轉(zhuǎn)換時(shí),應(yīng)謹(jǐn)慎操作,以免損壞原始數(shù)據(jù)。建議在轉(zhuǎn)換前備份原始文件,并在轉(zhuǎn)換后進(jìn)行驗(yàn)證以確保數(shù)據(jù)的完整性和準(zhǔn)確性。
本文主要講述使用Java編程語(yǔ)言進(jìn)行地理信息數(shù)據(jù)解析的時(shí)候,遇到Shapefile的屬性信息亂碼的幾種情況,以及根據(jù)不同的編碼設(shè)置來進(jìn)行屬性信息的解析。博文首先介紹采用不同的字符集編碼的shapefile文件,然后在Qgis中打開屬性表,查看相關(guān)的字符展示情況,接著說明在Java當(dāng)中調(diào)用Geotools時(shí),為經(jīng)過字符編碼處理和經(jīng)過字符編碼處理后的對(duì)比,讓大家熟悉在Geotools的開發(fā)過程中,掌握字符編碼的設(shè)置。
一、Shp文件常見的字符集編碼
為了講解使用不同的編碼來展示空間數(shù)據(jù),我們首先來介紹基礎(chǔ)的數(shù)據(jù),即三份不同的空間數(shù)據(jù)格式,其格式都是shapefile的。但是在不同的空間記錄中,其字段的值是采用不同的編碼的。在這里,采用QGIS這款軟件來進(jìn)行屬性數(shù)據(jù)的展示,方便大家了解日常中的數(shù)據(jù)展示。
1、System編碼
第一種要介紹的就是System的編碼方式,這里采用的是用我國(guó)的Lake圖層信息,首先我們?cè)赒gis中打開這份數(shù)據(jù)來看一下,文件的本地路徑為:
F:\vector_data\地理數(shù)據(jù)20240912\地理數(shù)據(jù)20240912\水系河流\1 全國(guó)1-5級(jí)標(biāo)準(zhǔn)河流-wgs84\主要湖泊面文件\Lake.shp
將數(shù)據(jù)在Qgis軟件中打開可以看到其主要的源信息描述如下:
可以在編碼一欄中看到,這份文件的編碼是System的。 為了看到其里面的屬性信息,可以右鍵點(diǎn)擊shp數(shù)據(jù),點(diǎn)擊打開屬性表就可以看到這份數(shù)據(jù)的完整的數(shù)據(jù)信息,打開后相關(guān)信息如下所示:
在上圖的紅線框中很明顯可以看到,有一列叫NAME的,它的值是有亂碼的,并沒有是我們常見的編碼。 因此這算是亂碼的第一種情況。
2、ISO-8859-1編碼
第二種也是常見的ISO-8859-1編碼,這里準(zhǔn)備的數(shù)據(jù)是一份湖南省的鄉(xiāng)鎮(zhèn)邊界數(shù)據(jù),其在文件磁盤中目錄如下,:
C:\BaiduDownload\湖南省\湖南省_鄉(xiāng)鎮(zhèn)邊界.shp
同樣的,我們使用QGis軟件打開上面的鄉(xiāng)鎮(zhèn)邊界.shp文件,打開后可以看到很明確的字符編碼信息:
同樣的我們使用QGIS來進(jìn)行屬性數(shù)據(jù)的打開查看, 詳細(xì)如下圖所示:
3、UTF-8編碼
這應(yīng)該算是比較標(biāo)準(zhǔn)的編碼方式,如果進(jìn)行數(shù)據(jù)制作的時(shí)候,都是統(tǒng)一采用UTF-8的模式,那么這種方式無疑是最好的,估計(jì)也不存在字符編碼的問題了。
這個(gè)時(shí)候,在QGIS中打開屬性表,其屬性字段的內(nèi)容是正??梢灾苯宇A(yù)覽的,詳情如下圖所示。
當(dāng)然,字符編碼的處理方式根據(jù)項(xiàng)目的不同,也會(huì)有不同的設(shè)置,種類繁多,不甚枚舉,這里僅以這幾項(xiàng)為例作為例子來講解,如果以后在開發(fā)過程中,遇到這種情況,可以根據(jù)實(shí)際來進(jìn)行編碼的擴(kuò)充和修復(fù)。
二、GeoTools解析實(shí)戰(zhàn)
在上面的一節(jié)中,我們簡(jiǎn)單的對(duì)三種不同的編碼方式的shapefile文件進(jìn)行了簡(jiǎn)單的介紹,本節(jié)則重點(diǎn)介紹如何使用Java開發(fā)語(yǔ)言,使用GeoTools的開發(fā)組件進(jìn)行編碼的處理和轉(zhuǎn)換,將屬性數(shù)據(jù)可以成功讀取到我們的應(yīng)用程序中。在這里需要統(tǒng)一說明的是,在進(jìn)行數(shù)據(jù)的處理和轉(zhuǎn)換的時(shí)候,為了進(jìn)行數(shù)據(jù)的演示,我們僅將數(shù)據(jù)的前10行數(shù)據(jù)記載處理,這樣如果有亂碼的問題,我們就可以直接進(jìn)行干預(yù),通過修改其它的字符集函數(shù)的方式來保證文字的識(shí)別與處理。
1、未進(jìn)行字符處理
首先我們來加載UTF-8的矢量數(shù)據(jù),測(cè)試一下使用UTF-8的情況下,如何使用GeoTools的方法來進(jìn)行屬性表格的解析。下面來看如何使用GeoTools來進(jìn)行空間屬性數(shù)據(jù)的解析與展示,關(guān)鍵代碼如下所示:
/** * * 不做任何處理展示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)建一個(gè)Query對(duì)象 Query query = new Query(typeName); // 設(shè)置查詢返回的最大特征數(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("-----------------------------------------------------"); } }
在上面的代碼中,需要注意的地方就是,我們想要在查詢的時(shí)候只查10條,那么就需要使用到GeoTools的查詢Query對(duì)象,通過結(jié)合Query對(duì)象來實(shí)現(xiàn)只查10條。10條的設(shè)置是個(gè)經(jīng)驗(yàn)值,可以根據(jù)服務(wù)器的速度和性能來進(jìn)行平衡,可以一次處理更多的數(shù)據(jù)。運(yùn)行測(cè)試用例來看其讀取的結(jié)果如下:
可以看到,在IDE的控制臺(tái)中,讀取出來的空間屬性信息都是亂碼。
2、亂碼問題的解決
要想解決亂碼的問題,首先要找到根源。我們需要對(duì)屬性信息字段進(jìn)行字符集編碼的控制。因此我們?cè)诨ヂ?lián)網(wǎng)上查詢一下,時(shí)候有相應(yīng)的方案。在這里哪怕不管具體的方案,也要了解為什么會(huì)出現(xiàn)這個(gè)問題。我們來看下ShapefileDataStore這個(gè)對(duì)象,這個(gè)對(duì)象是有一個(gè)關(guān)于字符集的編碼的,如下所示:
如果看過源碼的話,各位小伙伴會(huì)發(fā)現(xiàn),在GeoTools中有默認(rèn)的編碼集,即:Charset charset = DEFAULT_STRING_CHARSET;
public static final Charset DEFAULT_STRING_CHARSET = (Charset) ShapefileDataStoreFactory.DBFCHARSET.getDefaultValue();
其實(shí)現(xiàn)的實(shí)際邏輯代碼如下:
/** * 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(); } };
通過上面的代碼可以看到,這里使用的默認(rèn)編碼是:StandardCharsets.ISO_8859_1,也就是ISO-8859-1的方式。
3、轉(zhuǎn)碼支持
了解了亂碼的產(chǎn)生原理之后,我們來進(jìn)行相應(yīng)的代碼轉(zhuǎn)換,關(guān)于編碼的轉(zhuǎn)換有兩種方式,第一種統(tǒng)一在Store一層就進(jìn)行轉(zhuǎn)碼。這樣比較單一,也比較簡(jiǎn)單。第二種就是在每一個(gè)value中進(jìn)行編程式轉(zhuǎn)碼,這樣不僅麻煩,而且效率低。為了支持全局處理編碼等,我們將函數(shù)進(jìn)行已統(tǒng)一的封裝,增加了自定義編碼的支持:
/** * *展示shp文件數(shù)據(jù)詳情 * * @param shpFile shp文件地址 * @param unifySetting 是否統(tǒng)一設(shè)置字符 * @param chartSet 需要設(shè)置的字符編碼 * @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)建一個(gè)Query對(duì)象 Query query = new Query(typeName); // 設(shè)置查詢返回的最大特征數(shù)為10 query.setMaxFeatures(10); if (unifySetting) { store.setCharset(Charset.forName(chartSet));// 設(shè)置中文字符編碼 } 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、屬性字段編碼結(jié)果
下面對(duì)集中情況的屬性數(shù)據(jù)進(jìn)行解析,將輸出的成果在編輯器的控制臺(tái)進(jìn)行綜合展示。
可以在控制臺(tái)中看到以下的數(shù)據(jù)都是正常的,
org.geotools.data.shapefile.ShapefileFeatureStore@1b6e1eff the_geom===POINT (113.24947489555838 28.625229546432124) 名稱===南門橋(公交站) 大類===交通設(shè)施服務(wù) 中類===公交車站 小類===公交車站相關(guān) 地址===星通2路 省===湖南省 市===長(zhǎng)沙市 區(qū)===長(zhǎng)沙縣 WGS84_經(jīng)===113.249474896 WGS84_緯===28.6252295464
同理,其它的數(shù)據(jù)如湖南省鄉(xiāng)鎮(zhèn)邊界數(shù)據(jù),我們使用編碼后來查看具體的輸出。
同樣的,在控制臺(tái)中可以看到以下的輸出,
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語(yǔ)言進(jìn)行GeoTools解析Shp文件的屬性信息時(shí)亂碼的問題得到解決。
三、總結(jié)
以上就是本文的主要內(nèi)容,本文主要講述使用Java編程語(yǔ)言進(jìn)行地理信息數(shù)據(jù)解析的時(shí)候,遇到Shapefile的屬性信息亂碼的幾種情況,以及根據(jù)不同的編碼設(shè)置來進(jìn)行屬性信息的解析。博文首先介紹采用不同的字符集編碼的shapefile文件,然后在Qgis中打開屬性表,查看相關(guān)的字符展示情況,接著說明在Java當(dāng)中調(diào)用Geotools時(shí),為經(jīng)過字符編碼處理和經(jīng)過字符編碼處理后的對(duì)比,讓大家熟悉在Geotools的開發(fā)過程中,掌握字符編碼的設(shè)置。行文倉(cāng)促,定有許多不足之處,如有不當(dāng)之處,還懇請(qǐng)各位專家和博主在評(píng)論區(qū)留言支持,不勝感激。
博文在寫作過程中,參考以下,在此表示表示:
2、java gdal 創(chuàng)建shapefile屬性中文亂碼。
到此這篇關(guān)于在Java中基于GeoTools的Shapefile讀取亂碼的問題解決辦法的文章就介紹到這了,更多相關(guān)Java GeoTools的Shapefile讀取亂碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章

Java并發(fā)J.U.C并發(fā)容器類list set queue

SpringBoot中web模版數(shù)據(jù)渲染展示的案例詳解

java文件復(fù)制代碼片斷(java實(shí)現(xiàn)文件拷貝)

SpringCloud項(xiàng)目中Feign組件添加請(qǐng)求頭所遇到的坑及解決

Java super關(guān)鍵字調(diào)用父類過程解析

Java的MyBatis快速入門和實(shí)戰(zhàn)詳解

SpringBoot3.X配置OAuth的代碼實(shí)踐