基于Java标准库读取CSV实现天地图POI分类快速导入PostGIS数据库实战_java天地图类
目录
前言
一、天地图POI分类简介
1、数据表格
2、分类结构
二、从CSV导入到PG数据库
1、CSV解析流程
2、数据转换及入库
3、入库成果及检索
三、总结
前言
在之前的博客中,曾经对高德地图和百度地图的POI分类以及使用PostGIS数据库来进行管理的模式进行了详细的介绍。之前的博文列表:
相信大家在平时的日常生活中,用的比较多的肯定是百度地图和高德地图。虽然天地图在移动端的使用市场没有前两者的份额多。但是作为官方的标准,天地图还是拥有自己得天独厚的优势,除了本身最具权威的地理数据,同时还有承载着官方标准的执行。因此天地图的POI数据也是非常重要的,因此可以作为我们日常数据分析和处理的一个可靠的信息来源。不同的平台对POI的分级分类都有所不同,相信看过上面两篇博文的朋友一定知道,不同的厂商,对于POI分类时,它的大类和小类的定义一定是不一样的。但是天地图POI的分类存在非常大的差异,层次结构也是不一样的。如下图:
天地图的POI分类从大类来说就跟高德和百度不一样。单从一级大类的数量来说,百度拥有 32个,而高德只区分了25个,天地图居然有58个,在数量上天地图是比较多的(是否可以再精简和合并呢,需要讨论),比前两个加起来都多。另外从层级上来说,高德通常只区分了3级分类,而百度竟然有5级分类,然而天地图在层级上非常简单,只有2级展示,从扁平的角度来说,天地图的扁平化做的不错。
那么本文即来重点讲讲天地图POI分类与高德POI分类以及百度POI分类存在什么不一样的地方,同时结合代码深入讲解使用Java标准库来读取天地图的POI 分类兵如何进行数据导入到PostGIS空间数据库中,也为各类基于 POI 分类数据的地理信息系统开发、商业智能分析以及城市规划应用等,铺设一条从数据获取到存储利用的高效路径,助力行业在空间数据赋能下实现精准决策与创新发展。
一、天地图POI分类简介
本节将首先重点介绍天地图地图的POI分类信息,在之前的博客中我们设计了用于POI管理的物理表,这里可以继续用来存储天地图对应的POI分类信息。然后使用数据库脚本的方法对POI分类信息进行录入管理。对于天地图而言,其POI的分类较多,但是层级简单,因此这一节我们来详细的解读一下天地图的POI分类,让大家对分类信息有进一步的了解,为下一步对数据层级组装和批量解析入库打下牢固的基础。
1、数据表格
与之前介绍的内容一样,大家可以从天地图的地图开放平台中获取其最新的POI分类的CSV表格(是的,你没有看错,官方提供的确实是CSV而不是Excel,如果需要使用Excel也可以将CSV转一下格式),这里我将从官网下载的类型截取一部分给大家参考。下载链接传送门:分类编码表。下面来看下官方在数据检索的相关介绍:
1.1.1输入参数说明
这里不进行赘述,需要原始CSV表格的,可以去网站上下载。打开下载后的表格数据如下:
从上面这张图可以看出天地图的POI分类确实分的比较简单。 同时也能看到一个比较明显的区别,与百度的POI分类不一样的是,天地图有编码的概念。同样的,基于天地图地图的POI检索可以从返回接口中看到其对应的POI分类值为:
{\"eaddress\": \"\",\"address\": \"荣滨南路东段11号附28附近\",\"city\": \"\",\"provinceCode\": \"156500000\",\"cityCode\": \"\",\"county\": \"荣昌区\",\"typeName\": \"副食专卖店\",\"source\": \"0\",\"typeCode\": \"130207\",\"lonlat\": \"105.590250,29.416870\",\"countyCode\": \"156500153\",\"ename\": \"\",\"province\": \"重庆市\",\"phone\": \"18716232760\",\"poiType\": \"101\",\"name\": \"李氏卤鹅\",\"hotPointID\": \"50166082CE55CFF5\"}
其中typeCode对应poi分类的类别编码,而typeName则表示具体的类别名称。
2、分类结构
在了解了天地图的POI分类之后,下面我们基于之前设计的数据库物理表和分类信息构建树形的信息。因此需要对其分类采取细致的分类管理。在进行树形层次构建时,我们根据分类名称来进行统一管理:
这个结构是天地图POI分类管理的基础,也是后面的数据程序解析的基础。我们将使用编码来进行数据的解析及入库。 在CSV中,很大的大类和种类都是重复的,因此需要在入库时将类别进行去重分类,最终构建一棵完整的POI分类树。
二、从CSV导入到PG数据库
本节将详细介绍在Java中使用标准库实现从CSV中解析到存储至PostgreGIS中,主要包含两个方面,第一个是如何使用Java标准库来进行CSV解析。第二个方面是如何基于Mybatis实现程序的批量入库。关于数据处理流程与高德和百度POI入库的流程一致,基本分为三个步骤:第一步是批量读取数据源,第二步是将数据源解析出POI分类数据,最后将分类好的数据导入到PG数据库中。
1、CSV解析流程
首先还是对天地图下载的CSV格式的POI分类进行解析,在进行POI的分类进行构建时尤其重要,为了防止各层级在构建时出现重复的情况,这里采用LinkedHashMap集合来进行重复判断,在存储集合对象时,将分类编码作为map的key,而具体分类对象作为value。在天地图的POI分类编码中,可以发现将编码的前四位和后两位可以拆开,后两位如果是00的一般都是父类节点,通过我们的观察可以看到在后续的对象去重判断时,key就是重复的标记。为了实现从CSV格式的文件中读取数据,这里首先介绍一下CSV的解析流程。在正式讲解数据导入时,需要明确一个信息,就是在CSV数据的每一行中,只要遇到-这个字符就基本表示再往后读一个单元格,当前行的数据读取任务结束。
第一步:一些不要的辅助信息,比如需要定义需要处理的文件本地路径地址。代码如下:
@Autowiredprivate IPoiCategoryService poiCateGoryService;private static final String TDT_POI_CSV_FILE = \"C:/Users/Administrator/Desktop/天地图及相关信息/天地图Type-数据分类(分类编码表).csv\";private static final String TRIGGER_CHAR = \"-\";
第二步:CSV解析实现
为了能够正确的解析CSV数据,这里需要额外定外定义两个参数,以此保证数据的准确解析。关于CSV的解析实现代码如下:
// 自定义CSV行解析(处理带逗号的字段)private static String[] parseCsvLine(String line) { java.util.List fields = new java.util.ArrayList(); StringBuilder field = new StringBuilder(); boolean inQuotes = false; for (char c : line.toCharArray()) { if (c == \'\"\') { inQuotes = !inQuotes; // 切换引号状态 } else if (c == \',\' && !inQuotes) { fields.add(field.toString()); field.setLength(0); // 重置 } else { field.append(c); } } fields.add(field.toString()); // 添加最后一个字段 return fields.toArray(new String[0]);}// 清理单元格值private static String cleanCellValue(String value) { // 去除首尾空格和引号 value = value.trim(); if (value.startsWith(\"\\\"\") && value.endsWith(\"\\\"\")) { value = value.substring(1, value.length() - 1); } // 处理转义的双引号 return value.replace(\"\\\"\\\"\", \"\\\"\");}
第三步:CSV数据解析,实现最基本的数据解析读取和基础配置之后,下面就可以来实现对CSV数据的具体读取,这里采取的是直接的Java标准库解析的方法。实例代码如下:
private static LinkedHashMap csv2Map(){ // UTF-8, GBK, GB2312, ISO-8859-1, Windows-1252 Charset charset = Charset.forName(\"GBK\"); // 根据实际文件编码设置 LinkedHashMap amapPoiTypeMap = new LinkedHashMap(); try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(TDT_POI_CSV_FILE), charset))) { String line = \"\"; int rowNum = 0; while ((line = br.readLine()) != null) { rowNum++; String[] cells = parseCsvLine(line); // 使用自定义解析方法处理带逗号的字段 boolean foundTrigger = false; List poiCategories = new ArrayList(); String poiCategoryCode = \"\"; //获取每一行的的每个格子的数据 for (int i = 0; i < cells.length; i++) { String cellValue = cleanCellValue(cells[i]); //System.out.println(cellValue); if (cellValue.contains(TRIGGER_CHAR)) { foundTrigger = true; //System.out.println(\"[行 \" + rowNum + \"] 触发单元格: \" + cellValue); if (i + 1 < cells.length) { String nextValue = cleanCellValue(cells[i + 1]); poiCategoryCode = nextValue; //System.out.println(\"→ 终止单元格: \" + nextValue); } else { System.out.println(\"→ 终止单元格: (空)\"); } break; }else { poiCategories.add(cellValue); } } if (!foundTrigger) { System.out.println(\"行 \" + rowNum + \": 未找到触发字符\"); } //执行分类预处理 if(foundTrigger) { String prefix_head = poiCategoryCode.substring(0,4); String prefix_tail = poiCategoryCode.substring(4); if(prefix_tail.equalsIgnoreCase(\"00\")) {//00表示大类 String levelFirst = poiCategoryCode; //处理一级,添加到集合中 if(!amapPoiTypeMap.containsKey(levelFirst)) { PoiCategory category = new PoiCategory(IdWorker.getId(),1944421516292726785L,\"0,100,1944421516292726785\",String.join(\"/\", poiCategories),StringUtils.EMPTY,levelFirst); amapPoiTypeMap.put(levelFirst, category); } }else {//剩下表示小类 if(!amapPoiTypeMap.containsKey(poiCategoryCode)) { String _parentKey = prefix_head + \"00\"; PoiCategory parentCategory = amapPoiTypeMap.get(_parentKey); String ancestors = parentCategory.getAncestors() + \",\" + parentCategory.getPkId(); PoiCategory category = new PoiCategory(IdWorker.getId(),parentCategory.getPkId(),ancestors,String.join(\"/\", poiCategories),StringUtils.EMPTY,poiCategoryCode); amapPoiTypeMap.put(poiCategoryCode, category); } } } } } catch (IOException e) { e.printStackTrace(); } return amapPoiTypeMap;}
2、数据转换及入库
将CSV数据成功的解析转换成LinkedHashMap之后,事情还远远没结束,接下来需要将获取的LinkedHashMap转换成ArrayList。为了在查询数据的时候方便,在正式入库之前还需要增加一家额外的辅助信息,信息如下:
/*** - 将map集合转换成list集合* @param amapPoiTypeMap* @return*/private static List convertMap2DataList(LinkedHashMap amapPoiTypeMap) {List categoryData = new ArrayList(); Date now = DateUtils.getNowDate();for (PoiCategory value : amapPoiTypeMap.values()) { value.setPlatform(\"tianditu\"); value.setDelFlag(0); value.setStatus(0); value.setOrderNum(1); value.setCreateTime(now); categoryData.add(value); }return categoryData;}
到这里,我们就已经实现了如何使用Java标准库来解析CSV文件,并且将数据都读到了LinkedHashMap对象中,接下来就调用上述的方法来组装成一个完整的数据导入实例,核心代码如下:
@Testpublic void writer2DB() {//step1、读取CSV到Map集合中LinkedHashMap map = csv2Map();//step2、将map转成list集合List categoryData = convertMap2DataList(map);//step3、插入到数据库中for (PoiCategory poiCategory : categoryData) {System.out.println(poiCategory);}//step4、数据入库 poiCateGoryService.batchInsertPoiCategory(categoryData);System.out.println(\"finished...\");}
3、入库成果及检索
完成以上的程序编写,并运行操作后就完成了天地图POI分类数据的PostGIS数据库导入操作,程序执行完成后,可以在控制台看到以下输出:
为了验证是否在数据库中是否也保存了这些数据,可以使用以下SQL语句进行查询:
select * from biz_poi_category t where platform = \'tianditu\';
在客户端软件中执行以上SQL后可以看到以下结果:
三、总结
以上就是本文的主要内容,支持对天地图的POI分类的CSV入库及检索就基本完成,后续我们将深入使用POI信息以及如何进行相应数据的采集。那么本文即来重点讲讲天地图POI分类与高德和百度POI分类存在什么不一样的地方,需要注意的是,本文读取的文件不是Excel而是CSV格式,因此也深入讲解了如何使用Java标准库来解析天地图的POI分类文件,同时深入讲解天地图 POI 分类如何进行数据导入,如何将CSV数据转为LinkedHashMap,再到ArrayList,最后批量入库。也为各类基于 POI 分类数据的地理信息系统开发、商业智能分析以及城市规划应用等,铺设一条从数据获取到存储利用的高效路径,助力行业在空间数据赋能下实现精准决策与创新发展。行文仓促,难免有许多不足之处,如有不足,在此恳请各位专家博主在评论区不吝留言指出,不胜感激。