简介:一份围绕“天地图·常州”的地理数据解析与聚合方法研究PDF,聚焦大数据算法在地理信息公共服务平台中的应用,适合地理信息、数据挖掘及智慧城市方向的研究者、平台开发者和相关专业学生。该研究针对“天地图”基础测绘数据难以满足公众服务与行业应用的问题,从地理数据资源分类入手,梳理了电子地图、数字正射影像等基础数据与团购、房产、公交、公众点评等公共服务类数据之间的差异,并围绕在线服务数据集和在线文本数据,提出了获取、解析与聚合的技术方案。内容同时结合“天地图”国家、省、市级节点建设要求,设计了面向公众服务的地理数据解析与聚合原型系统,并通过常州实例展示了团购、房产、公交、公众点评等多源数据在同一平台上的集成效果,读者可据此理解从数据来源识别、结构解析到服务聚合的完整链路。资源包仅为1个PDF文件,大小约3.36MB,目前已有73人学习下载。
1. 从基础测绘到生活数据:天地图地理数据聚合要解决什么问题
“天地图”国家、省、市三级节点的在线数据集,长期以来以基础测绘成果为主,电子地图、数字正射影像、数字高程模型支撑了地图浏览和路径规划,但公众真正高频使用的团购门店位置、二手房挂牌价、公交实时站点、餐饮点评热度,恰恰不在主节点的数据体系里。这篇论文以“天地图·常州”为例,把团购、房产、公交、公众点评四类社会公共地理数据,通过HTML、XML、JSON解析后按天地图的服务框架聚合发布,最终在市级节点上形成可用的专题服务。对做GIS平台、数据中台或地图应用开发的人,这篇论文的价值在于:它不是从零搭平台,而是围绕“已有平台如何吃进外部异构数据”给出了一条从解析、空间化到服务聚合的完整路线,其中对多源数据字段映射、坐标纠偏和服务接口封装的取舍,放在目前的时空数据项目里依然能直接复用。
2. 地理数据资源分类与选型:政府数据与社会数据的分野
2.1 天地图三级节点建设的数据基线与坐标系约束
“天地图”国家主节点、省级分节点和市级信息基地采用同一套空间基准,即2000国家大地坐标系(CGCS2000),在线数据集包含地理实体数据、地名地址数据、线划电子地图数据、影像电子地图数据、POI数据、三维建筑物模型和街景数据。对外服务统一走OGC标准地图服务接口,包括WMS、WMTS、WFS、WFS-G、CSW,以及面向客户端的JavaScript API、Flex API、Silverlight API和Android API。这意味着任何外部地理数据要进入“天地图·常州”平台,第一步不是做漂亮的可视化,而是把坐标系、数据格式和服务接口三件事对齐:坐标系必须转换到CGCS2000,数据格式要能被WMTS/WFS这类接口承载,访问方式要符合平台已有的API契约。论文把这一前提作为解析和聚合的约束条件来处理,这个顺序值得借鉴——很多项目先把爬虫写完了,回头才发现坐标系不一致导致点位整体偏移,返工成本远高于提前做数据基线检查。
2.2 政府公共地理数据资源的底图价值
政府公共地理数据源于基础测绘,产品形态包括数字线划图(DLG)、数字正射影像(DOM)、数字高程模型(DEM)和POI数据。这些数据的共同特点是精度高、权威性强、更新节奏慢,适合作为聚合应用的底图和空间参考,但不足以单独支撑面向公众的专题服务。以“天地图·常州”为例,论文整理了DLG和航片的数据情况,道路、水系、境界等矢量要素按年度更新,影像数据按季度到年度更新,更新频率远远赶不上团购活动调整或者商铺搬迁的速度。
| 数据类别 | 典型产品 | 更新节奏 | 在聚合中的角色 |
|---|---|---|---|
| 数字线划图(DLG) | 道路、水系、境界矢量 | 年度 | 底图要素、空间参照 |
| 数字正射影像(DOM) | 航空影像、卫星影像 | 季度/年度 | 影像底图、效果校验 |
| 数字高程模型(DEM) | 地形高程栅格 | 低频 | 三维地形、坡度分析 |
| 兴趣点(POI) | 门牌、设施点 | 月度 | 地址匹配、空间化锚点 |
从聚合的角度看,政府数据真正值钱的地方是POI和地名地址数据,它们可以作为社会数据空间化的“锚点”:团购数据里只有文字地址,通过地名地址匹配到POI坐标,才能落到地图上。这一点在论文第4章的聚合框架中体现得很明显,地名地址数据聚合技术路线图里,匹配和纠偏始终是核心环节。换句话说,政府数据不直接参与业务展示,但社会数据的空间化质量完全取决于地名地址库的完整程度。
2.3 社会公共地理数据资源的选型标准
社会公共地理数据来自互联网,论文从中挑选了团购、房产、公交、公众点评四类。选型标准可以归纳为三条:第一,数据里包含明确的空间位置字段,至少有地址文本,最好有经纬度;第二,数据更新频繁,能反映城市生活的动态变化;第三,公众需求度高,能补齐基础测绘底图之外的“生活图层”。四类数据的对比关系如下。
| 数据源 | 关键属性 | 更新频率 | 结构化程度 |
|---|---|---|---|
| 团购数据 | 商家名、地点、价格、销量 | 小时级 | 半结构化HTML/JSON |
| 房产数据 | 小区、户型、单价、总价 | 日级 | 半结构化HTML/JSON |
| 公交数据 | 线路、站点、首末班时刻 | 调整期日级 | 结构化JSON/XML |
| 公众点评数据 | 商户、评分、评论数、地址 | 分钟级 | 半结构化HTML |
判断一份数据值不值得解析,主要看两个信息面:结构信息和属性信息。结构信息解决“怎么取”的问题,比如团购列表页里的分页结构、商家详情页里的地址字段;属性信息解决“取出来之后怎么用”的问题,比如房产数据的单价、朝向、面积可以支撑后续的空间统计分析。论文把这两类信息分开处理,解析时不追求一次到位,而是先摸清接口返回的结构,再按聚合需要抽取属性字段,这种做法在面对来源混杂的在线数据时更容易收敛,也方便后期增加新的数据源。
3. 在线地理数据的解析方法:从HTML页面到结构化空间数据
3.1 解析框架:数据获取、数据抽取、聚合服务与客户端四层拆解
在线数据解析最大的难点不是某个具体语法,而是来源差异。团购页面的HTML结构、公交接口的XML报文、点评平台的JSON响应,看起来完全没有共性,如果为每个数据源各写一套硬解析逻辑,后续维护就是灾难。论文将“天地图·常州”的地理数据抽取与聚合拆成四个环节:数据获取、数据抽取、聚合服务和客户端聚合。数据获取负责从目标网站或接口拉取原始内容,可能是HTML页面、XML文档或JSON字符串;数据抽取负责从原始内容中剥离出结构化的业务字段,比如店名、地址、价格、评分;聚合服务负责把清洗后的数据转换成符合天地图服务规范的能力,比如提供按行政区划查询团购信息的接口;客户端聚合负责在前端把多个服务的结果叠加展示,并做点位聚合、筛选排序等交互处理。分层之后每一层的替换成本都低:数据源接口变了,只改数据获取层;展示样式变了,只改客户端聚合层,中间的结构化数据和服务接口保持稳定。
3.2 HTML解析的三种处理思路
3.2.1 直接抽取与XML文档转换
基于HTML的解析,最直接的是直接抽取,依赖页面标签的层次结构,通过自动或半自动的方式生成抽取规则,比如定位到class为“deal-title”的标签提取团购标题。这种方式的优点是实现快,缺点是页面改版后规则立刻失效。更稳健的做法是先利用XML技术把HTML转换为结构化的XHTML文档,再按XML的规范解析,因为HTML的标签闭合不规范,直接解析容易出错,转成XHTML之后可以用XPath批量定位节点。下面是抓取团购页面商家地址的示例。
import requests from bs4 import BeautifulSoup resp = requests.get( "https://example.com/changezhou/deals", headers={"User-Agent": "Mozilla/5.0"}, timeout=10 ) soup = BeautifulSoup(resp.text, "html.parser") for deal in soup.select("div.deal-item"): title = deal.select_one(".deal-title").get_text(strip=True) price = deal.select_one(".deal-price").get_text(strip=True) address = deal.select_one(".deal-shop-address").get_text(strip=True) print(title, price, address)这段代码用requests拉取团购列表页,用BeautifulSoup按CSS选择器抽取标题、价格和地址。select_one定位单个节点,get_text(strip=True)去除空白字符。实际项目里要注意两点:一是User-Agent要模拟浏览器,否则容易被反爬拦截;二是选择器要基于稳定的class或data属性,避免依赖纯粹的样式类名,否则前端稍作重构抽取规则就失效。如果要用XPath方式解析,论文提出的思路是先做HTML到XHTML的转换,再借助XPath表达式定位节点,这一方案在页面结构规范的数据源上表现比CSS选择器更稳定。
3.2.2 基于Ontology的概念建模
针对抽取规则易失效的问题,论文提到了基于概念建模的HTML解析方法,利用本体(Ontology)先建立数据模型,再把页面上可能抽取的数据项映射到本体元素上。比如定义“商家”这个本体包含名称、地址、经纬度、营业时间等属性,然后把页面中的不同标签映射到对应属性。这样不同来源的数据经过映射后,都能以统一的视图呈现,信息继承和交换就方便了。Ontology方案在数据源多、字段杂、且对一致性要求高的场景里很有价值,代价是需要先投入成本维护本体模型,对只有两三个数据源的市级节点项目来说,直接用JSON Schema约束字段可能更务实。这里的关键判断是:数据源数量少且固定时,规则解析足够;数据源持续扩张时,本体模型的前期成本才值得。
3.3 XML解析的DOM与SAX取舍
XML解析常见的有DOM和SAX两种方式。DOM把整个文档读入内存构建文档树,修改方便,但文档一大内存就紧张;SAX是顺序流式解析,加载和解析同时进行,不要求一次性载入,适合大文件,但只能顺序读一遍,不支持随机访问和修改。论文还提到基于XmlTextReader类的读取方式,它提供快速、无缓存、单向的流式读取,本质上也属于顺序访问,和SAX的定位类似。实际处理公交数据这种按线路、站点组织的XML文档时,如果文件不大且需要频繁修改属性,用DOM更顺手。
from xml.dom import minidom doc = minidom.parse("bus_line.xml") lines = doc.getElementsByTagName("line") for line in lines: name = line.getAttribute("name") stations = line.getElementsByTagName("station") station_names = [s.getAttribute("name") for s in stations] print(name, len(station_names), station_names)这段代码用minidom.parse一次性载入XML,按line标签遍历线路,再用getAttribute读取线路名和站点名。参数说明:getElementsByTagName返回节点列表,getAttribute读取节点属性。当单个线路文件超过几十MB时,建议换成xml.sax或ElementTree.iterparse流式处理,避免内存占用过高。论文对三种XML解析方式的对比结论可以复用在大多数场景:数据规模小、需要修改时选DOM;数据规模大、只读顺序处理时选SAX;接口返回的单条XML报文用XmlTextReader这类流式读取直接抽取即可。
3.4 JSON解析与团购数据接口映射
JSON是轻量级数据交换格式,基于“名称/值”对集合和值的有序列表两种结构,前端可以用JavaScript的JSON.parse直接解析。论文给出的团购数据来源网站中,拉手网等平台提供城市列表、团购列表、商家详情等多个接口,返回大多是JSON,解析成本最低。实际处理时,Python用json.loads把响应体转为字典,关键是要维护好字段映射关系。
import json import requests resp = requests.get( "https://example.com/api/deals", params={"city": "changzhou", "page": 1, "pageSize": 50}, timeout=10 ) data = json.loads(resp.text) deals = [] for item in data["data"]["deals"]: deals.append({ "deal_id": item["deal_id"], "title": item["title"], "price": float(item["price"]), "sold": int(item["sold_count"]), "lat": float(item["shop"]["latitude"]), "lng": float(item["shop"]["longitude"]), "address": item["shop"]["address"] })这段代码把团购接口返回的JSON转成统一的内部字典结构。参数说明:params中的city是城市代码,page和pageSize控制翻页;请求时把页码、城市代码做成配置项而不是硬编码,方便切换城市。转换后的字典再写入PostGIS或者GeoJSON文件,后续空间化时直接使用lat和lng字段,不再需要做地址解析。论文中团购数据的结构设计也是沿着这个思路,先归纳接口返回的结构信息,再按聚合需要抽取属性,接口对照表的形式在下面列出,方便实际开发时按图索骥。
| 接口/页面 | 返回格式 | 关键字段 |
|---|---|---|
| 城市列表接口 | JSON | cityId、cityName、lat、lng |
| 团购列表接口 | JSON | dealId、title、price、value、sold |
| 商家详情页 | HTML | shopName、address、telephone |
| 地图坐标接口 | JSON | latitude、longitude、poiId |
4. 地理数据聚合:从服务链到天地图市级节点落地
4.1 地理空间服务聚合的服务链模型
地理空间服务聚合不是一个新概念,ISO 19119标准给出了服务链(Service Chaining)模型,后一个服务的执行依赖前一个服务的成功返回,形成顺序编排;在此基础上,BPEL(业务流程执行语言)被用来描述和执行空间服务链,将多个地图服务编排组合成新的服务并对外发布。论文在梳理国内外研究后指出,过去的研究多集中在OGC标准地理空间服务上,针对地理信息公共平台空间服务的聚合方法偏少。因此“天地图·常州”的方案没有照搬BPEL的复杂编排,而是采用“服务端聚合+REST接口暴露+客户端聚合展示”的组合,更适合市级节点这种轻量级场景。这里的选型逻辑是:服务链适合跨部门、跨组织的服务编排,而同一平台内部的多源数据聚合,用统一的数据模型和接口层就能解决问题,引入BPEL反而增加部署和运维成本。
4.2 天地图·常州聚合框架与数据空间化
聚合框架从下往上分四层:数据层存放解析后的团购、房产、公交、点评数据;解析层负责多源数据获取、字段清洗和格式统一;聚合服务层把数据发布成天地图可调用的服务接口;展示层在“天地图·常州”门户上叠加专题图层。各类数据的聚合方式不完全相同,政府公共地理数据聚合主要做基础地理信息、城市三维和地名地址的整合,社会公共地理数据聚合则以业务专题图层为主。空间化是聚合的核心环节,流程是:地址匹配获取坐标,坐标转换统一到CGCS2000,最后生成要素写入空间数据库。
import json from shapely.geometry import Point features = [] for deal in deals: point = Point(deal["lng"], deal["lat"]) features.append({ "type": "Feature", "geometry": point.__geo_interface__, "properties": { "deal_id": deal["deal_id"], "title": deal["title"], "price": deal["price"] } }) geojson = {"type": "FeatureCollection", "features": features} with open("deals.geojson", "w", encoding="utf-8") as f: json.dump(geojson, f, ensure_ascii=False)这段代码把上一步解析出的团购记录转成GeoJSON要素集,Point接收经度、纬度,properties里只保留聚合展示需要的字段。参数说明:deals是解析后的字典列表,feature的geometry采用GeoJSON标准,ensure_ascii=False保证中文正常写入。这里需要特别提醒,写入前务必检查数据源提供的是WGS84坐标还是火星坐标系,如果是后者,必须先做坐标转换,否则点位会整体偏移几百米,公交数据里这类问题尤其常见。论文在公交数据聚合部分专门给出了纠正前后的对比图,纠偏后线路走向与底图道路基本重合,这说明空间化阶段的质量控制直接影响最终展示效果。
4.3 房产与公交数据的聚合服务接口设计
论文在聚合应用部分给出了团购、房产、公交、公众点评四类数据的服务设计。以公交数据为例,接口参数需要覆盖线路、方向、站点序号和返回格式,下面的表格是聚合服务接口的典型参数设计。
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| lineName | string | 是 | 线路名称,如“B1”、“11路” |
| direction | int | 否 | 0表示上行,1表示下行 |
| stationSeq | int | 否 | 站点序号,从1开始,缺省返回全线路站点 |
| cityCode | string | 是 | 城市代码,“cz”表示常州 |
| output | string | 否 | 返回格式,支持json和geojson |
接口实现时,常见做法是用Flask这类轻量框架把聚合逻辑包成REST服务,查询数据库后直接返回GeoJSON,前端不用关心数据来源是公交公司还是第三方接口。设计要点有两个:一是参数默认值要保守,direction缺省时返回双向数据还是仅上行,必须在接口文档里写清楚,否则前端拿到数据后容易和预期不一致;二是cityCode这类参数预留出来是为了后续从常州扩展到其他城市,字段命名尽量统一到平台已有的规范上。房产数据的聚合服务设计也类似,只是在数据结构上多出户型、面积、单价等字段,接口适配的重点从线路切换变成字段映射。
4.4 聚合查询与Leaflet点位聚合展示
数据进库之后的查询,可以充分利用数据库的聚合函数来减少前端计算量。比如统计每个行政区划内的团购数量、平均折扣率,可以直接在SQL里用GROUP BY完成,而不是把明细拉到前端再算。这种方式对海量点位的聚合场景尤其重要,接口返回的已经是聚合结果而不是原始点集,网络传输和渲染压力都会小很多。
SELECT district, COUNT(*) AS deal_count, ROUND(AVG(price), 2) AS avg_price, ROUND(AVG(discount), 3) AS avg_discount FROM deals WHERE city_code = 'cz' GROUP BY district HAVING deal_count > 0 ORDER BY deal_count DESC;这段SQL按行政区划分组统计团购数据,COUNT(*)统计数量,AVG(price)计算平均价格,HAVING过滤空分组。参数说明:district字段在数据入库时通过地址匹配得到,city_code区分城市。如果查询频繁且数据量大,建议在city_code和district上建联合索引,或者用CTE预聚合方式先生成每天的快照表,避免每次请求都全表扫描,这也是大数据量下地图聚合展示的常用优化手段。前端展示层面,面对几千个商家点位逐点画marker会卡顿,论文的聚合应用中用客户端聚合处理这类展示问题。用Leaflet时可以直接引入MarkerCluster插件,把邻近点位聚合成数字气泡,缩放时再拆分,配合天地图瓦片底图使用,整体交互和加载性能都能接受,这也是当前地图应用里最稳妥的点位聚合方案。
5. 聚合应用的排错与优化:坐标纠偏、参数适配与验证技巧
5.1 公交数据坐标偏移纠正
公交数据源经常直接给出经纬度,但这些坐标未必落在CGCS2000或WGS84基准上,常见偏差是坐标整体平移几百米,表现是线路线位和底图道路不贴合。纠正办法有两种:一是拿已知正确坐标的站点做控制点,计算偏移量后批量修正;二是用公交站点名称匹配POI坐标,逐点校正。论文在公交数据聚合中单列了纠正前后的对比,纠正后的线路走向与底图道路基本重合,这一步对公交专题图层来说是不可省略的。
def correct_offset(lat, lng, offset_lat=0.0021, offset_lng=-0.0038): return lat + offset_lat, lng + offset_lng这段代码演示的是固定偏移量纠正,offset_lat和offset_lng来自控制点比对。注意固定偏移只适合同一数据源、同一坐标基准的情况,如果数据源混用了不同坐标系,要按来源分别维护偏移参数,不能一个参数套到底。更稳妥的做法是先抽样对比,确认偏移方向稳定后再批量纠偏,纠偏前后各保存一份数据,方便回溯对比。
5.2 聚合服务参数适配流程
外部数据字段名五花八门,进入聚合服务前必须做参数适配。流程一般是:拉取原始字段清单,对照内部标准字段建立映射表,再处理单位、格式和空值。房产数据里价格单位可能是“元/㎡”也可能是“万元”,点评数据里评分可能是5分制也可能是10分制,这些都需要在适配层统一。适配层不建议用大量if-else堆逻辑,最好用一张字段映射表驱动转换,新增数据源时只改配置不动代码,这样可以明显降低多数据源接入的维护成本。
5.3 天地图API调用排查与验证
聚合服务发布之后,验证环节容易被忽略。最直接的验证是请求天地图瓦片或调用聚合服务接口,确认返回结果和地图叠加位置正确。常见的问题是Key配置错误,调用天地图API时如果返回“code: 301001 / 非法key”,先检查三件事:Key是否在控制台正确创建、是否绑定了域名白名单、请求的URL域名和端口是否与白名单一致。本机调试时用localhost、线上用正式域名,这两者的白名单配置经常混在一起导致服务偶发失败。
curl "https://api.tianditu.gov.cn/v4/map/vec?tk=YOUR_KEY&x=1&y=1&l=4"这条命令直接验证天地图矢量瓦片服务能不能通,tk是申请的密钥,x、y、l是瓦片行列号和层级。返回图片流说明网络和Key都正常;返回JSON错误码就按错误码定位问题。用脚本巡检瓦片服务,比在浏览器里肉眼确认更自动化,也更适合聚合应用上线后的持续监控,聚合服务的数据源一旦变动,这类检查脚本能第一时间暴露坐标偏移或者接口异常。
本文还有配套的精品资源,点击获取