前言
基于前一篇文章GeoServer系列-通过mongodb发布geojson数据,业务上可将常见的地理文件统一为geojson保存到mongodb,方便统一维护和发布geoserver,这一篇将解决mongodb中属性中文乱码问题。
1,解决思路
- 因为很多空间数据是以压缩包上传,解压后转为geojson文件可能会很大(我的400+MB的gdb压缩包转换为geojson变为2.2GB),所以采用逐行读取的方式降低内存消耗
- 文件上传过程中多次涉及io操作:保存分片->合并分片->格式转换->坐标转换->保存为geojson,这个过程中都不需要设置编码方式,因为最后一步都是以geojson格式入库,所以只需要解决geojson乱码
- 文件乱码的问题核心是读取方式与文件本身的编码方式不一致导致,比如用户文件是GBK编码,gdal是用ISO8859-1读取文件,读取geojson属性又是用utf-8就必然乱码,所以核心问题是找到文件原来的编码方式,它怎么编码我们用什么编码读取
2,逐行读取geojson保存mongodb
/**
* 保存geojson数据到mongodb
*
* @param filePath geojson全路径
* @param collect 集合名 = 文件md5标识
* @return 属性列表
*/
public List<String> geojsonToMongodb(String filePath, String collect) throws IOException {
List<String> titleList = new ArrayList<>();
// 1. 读取 GeoJSON 文件,判断文件的编码方式确定读取编码
String chart = ReadUtil.detectCharset(new File(filePath)).name();
if("ISO-8859-1".equals(chart)){chart="GB2312";}
FileInputStream inputStream = new FileInputStream(filePath);
InputStreamReader inputStreamReader = null;
inputStreamReader = new InputStreamReader(inputStream, Charset.forName(chart));
//2. 连接 MongoDB ,如果集合已存在先清空
mongoTemplate2.dropCollection(collect);
mongoTemplate2.createCollection(collect);
//3 创建2dsphere索引
GeospatialIndex index = new GeospatialIndex("geometry");
index.typed(GeoSpatialIndexType.GEO_2DSPHERE);
mongoTemplate2.indexOps(collect).ensureIndex(index);
//4逐行读取feature,并写入Mongodb
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line;
int successNum = 0;
int failNum = 0;
while ((line = bufferedReader.readLine()) != null) {
// 判断当前行是否为一个 feature
if (line.trim().startsWith("{") && (line.trim().endsWith("},") || line.trim().endsWith("}"))) {
// 将当前行转换为 JSONObject
JSONObject feature = new JSONObject(line);
//自定义属性追加上feature唯一ID(用于地图联动)和集合类型
int pid = (int) feature.get("id");
String type = feature.getJSONObject("geometry").get("type") + "";
feature.getJSONObject("properties").put("id", pid).set("type", type);
Document document = Document.parse(feature.toString());
try {
mongoTemplate2.insert(document, collect);
successNum++;
} catch (Exception e) {
//忽略某些异常的Feature
//log.error("mongodb插入geojson数据失败:{}", e.getCause().getMessage());
failNum++;
}
//保存属性数据(仅第一列即可)
if (successNum == 1) {
JSONObject jsonObject = feature.getJSONObject("properties");
Set<String> sIterator = jsonObject.keySet();
for (String key:sIterator) {
titleList.add(key);
}
}
}
}
log.warn("插入geojson数据{},成功{}条,失败{}条", collect, successNum, failNum);
bufferedReader.close();
inputStreamReader.close();
return titleList;
}
某些字符编码存在包含关系,如
GB2312 是 ISO-8859-1 的子集, GB2312 中的所有字符都在 ISO-8859-1 中存在,但是 ISO-8859-1 中有一些 GB2312 中不存在的字符,这种情况需要特殊
3,获取文件的编码方式
import com.ibm.icu.text.CharsetDetector;
import com.ibm.icu.text.CharsetMatch;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
public static Charset detectCharset(File file) throws IOException {
CharsetDetector detector = new CharsetDetector();
byte[] buffer = new byte[4096];
try (FileInputStream input = new FileInputStream(file)) {
int nread;
while ((nread = input.read(buffer)) != -1) {
detector.setText(buffer);
CharsetMatch match = detector.detect();
if (match != null) {
return Charset.forName(match.getName());
}
}
}
return Charset.defaultCharset();
}
<dependency>
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
<version>72.1</version>
</dependency>
4,查看所有编码方式
@Test
public void test(){
Map<String, Charset> charsets = Charset.availableCharsets();
for (Map.Entry<String, Charset> entry : charsets.entrySet()) {
String name = entry.getKey();
Charset charset = entry.getValue();
System.out.println(name + ": " + charset.displayName() + ", " + charset.aliases());
}
}