☰
城市地图集制作指南:数据清洗、坐标系与开源工具实战
2026/10/19 17:03:00 网站建设 项目流程

Atlas这个词,在地图相关词汇里应该算得上最有故事感的一个。它既是古希腊神话里用肩膀撑起天空的泰坦神,也指把世界装订成册的地图集。第一次见到这个词是在一本旧地图册的书脊上,封面上只有一个单词,打开以后,一个城市的风貌、河流走向、道路脉络,全被安排得明明白白。后来我逐渐意识到,atlas的本质其实是“有组织的信息合集”,这也是我这些年一直喜欢做地图项目的原因。

这篇内容适合想用地图讲一点故事的人,不管你是做数据可视化、做城市观察,还是纯粹想把某个区域的POI整理成一份有质感的小册子,都可以参考。我会把一个地图集项目的完整链路拆开讲清楚:从数据怎么整理、坐标系为什么不能乱用,到地图配色怎么不翻车,再到最后怎样输出一份能拿得出手的作品。整条路径不依赖重型商业软件,绝大多数环节用开源工具和一段普通代码就能完成。

1. 一张atlas的底层逻辑:内容、媒介和表达

很多人在做“地图”和做“地图集”之间没有明确区分,上手就开始堆点,堆到最后像一张密密麻麻的仪表盘,信息全有,但读者看完就忘。真正的地图集作品,我在实际项目里总结下来,至少要同时处理好三个层次。

1.1 地图集的三个层次:底图、数据层、故事线

第一个层次是底图,也就是地理基底。它负责提供空间参照,让人知道“我在哪”。底图不能抢戏,一个常见的错误是把底图做得五颜六色,专题数据反而看不清。第二个层次是数据层,这是地图集的主题内容,可以是咖啡店分布、某区域的人流密度、骑行路线,或者任何你想表达的空间信息。第三个层次是故事线,这一点最容易被忽略。地图集的“集”字意味着它不只是一张图,而是多张图之间有编辑意图,读者翻过去的时候,应该能感受到你想强调什么、沿着什么顺序展开。

举个我自己的例子。我做过一份某个街区的地图集,数据本身只是几十家餐饮店的评分和品类。最开始我按照“点大小=评分”的逻辑去做,结果画面很乱。后来我换了个思路,把故事线定为“从早餐到夜宵”,每一页是一类业态,底图完全一致,但数据层依次切换。这样读者跟着翻页,就像沿着时间线逛了一遍街区。故事线一旦清晰,之前很多视觉决策都变得顺理成章。

我建议在动手前用三句话写下这张地图集的“编辑说明”:这个作品给谁看、他想获得什么信息、翻完之后应该留下什么印象。这三句话不需要给任何人看,但能帮你过滤掉大量无效工作。

1.2 技术路线怎么选:静态渲染、交互地图还是打印小册子

地图集的交付形式决定了整个技术栈。我通常把项目分成三类:静态渲染图、交互网页、适合打印的PDF小册子。三者不是互斥的,很多项目会同时用到两种形式,但一开始你最好想清楚核心交付是什么。

静态渲染图适合快速验证设计方向,用QGIS搭好样式,导出高分辨率图片,一般用于封面、海报或报告配图。交互网页适合信息量大、需要读者自己探索的场景,技术选型上我更偏向MapLibre GL,它轻量、开源,样式配置灵活,没有太多授权负担。打印小册子是最有“atlas”仪式感的形式,但也是最容易被低估的环节,因为地图上的文字大小、线条粗细都要以实际打印尺寸为标准,屏幕上看得很舒服的元素,印到A4纸上可能糊成一团。

我也做过一条偷懒的路径:先用交互网页把数据层调试到位,再把这套样式平移到QGIS布局管理器导出PDF。这样做的好处是,交互环境中能快速验证颜色、层级、标注避让是否合理,省去了反复打印测试的成本。如果你没有打印需求,可以跳过小册子部分,但前两类的沉淀一定不会白费。

2. 数据准备:这个环节直接决定成败

地图项目里最不性感但最要命的就是数据准备。一个地图集项目的时间分配,我见过太多人把80%时间花在调整样式上,结果最后卡在数据质量上。实际上,合理的时间分配应该是数据处理占六成,设计排版占三成,部署发布占一成。

2.1 数据源与字段设计,别拿到什么就喂什么

数据来源通常有三类:公开数据、开源社区数据、自己采集的数据。无论来自哪里,都需要先想清楚字段结构。我常用的最小字段集是:名称、类别、经度、纬度、备注、链接。经纬度建议分开存成两个字段,不要合并成一个坐标字符串,否则后续清洗和筛选会非常痛苦。

举个例子,假设你要做一份城市咖啡地图集,原始数据表里可能混杂着“咖啡”“奶茶”“甜品”多个分类。你一步到位只保留咖啡,会让后期想扩展维度时很被动。更好的做法是保留完整的业态分类,用category字段区分,后期通过过滤来控制显示内容。这样今天做“咖啡地图集”,明天想切换成“甜品地图集”,只需要换一下过滤条件,数据完全复用。

有个安全前提我必须专门说:个人地图项目务必使用来源明确、合法合规的数据,不要抓取涉及人群精确位置或敏感设施的信息。地图能承载的内容很多,但边界也很清楚,做作品不用冒着踩线的风险。

2.2 坐标系和投影,入门必须过的一关

坐标系是地图新手翻车率最高的地方,没有之一。很多人把经纬度数据直接扔进地图引擎,结果点显示到了海中央,或者跟底图错位好几条街。要彻底搞懂这个问题,可以做一个生活化类比:地球是橘子的果肉,球面是不可展开的,你要把皮剥下来平铺成一张纸,就必须在某个位置“撕开”,不同撕法就是不同投影。

实际使用中,你只需要分清两类坐标。一类是WGS84经纬度,也就是GPS设备直接给出的数据,通常用EPSG:4326表示,大多数外部数据源拿回来都是这个格式。另一类是Web墨卡托投影坐标,通常用EPSG:3857表示,几乎所有网页电子地图底图都基于这个投影。存储数据时,我强烈建议统一用EPSG:4326,因为经纬度是人类可读的、全球统一的;显示时,交给地图引擎自动转换到EPSG:3857。

很多人犯的错误是看到某API返回了“x”“y”字段就以为是经纬度,其实是投影坐标,直接当作纬度经度用,点自然就飞了。遇到这类数据,先判断数值范围:经度在-180到180之间,纬度在-90到90之间,绝对值一旦超过这个范围,说明大概率是投影坐标,需要先做转换再入库。

2.3 清洗坐标数据的标准动作

清洗数据不是要把数据洗得多漂亮,而是确保地图引擎能吃下去且不乱跑。我的标准动作只有四步,但每一步都有明确目的。

第一步是去重,按“名称+经纬度”组合判断,避免同一家店因为数据源叠加出现两次。第二步是剔除缺失值,只要名称、经纬度任意一项为空,直接整行丢弃,因为地图上不能显示一个没有坐标的信息点。第三步是范围校验,经纬度越界的数据多写一句纠正逻辑,比如经纬度写反的情况。第四步是统一坐标系,入库前确保所有数据都是EPSG:4326。

我给你一段可以直接用的数据清洗代码,使用GeoPandas处理,输出GeoJSON,这也是后续所有地图工具都能读取的通用格式:

import pandas as pd import geopandas as gpd # 原始数据读取,注意编码格式 df = pd.read_csv('poi_raw.csv', encoding='utf-8-sig') # 第1步:丢弃关键字段为空的行 df = df.dropna(subset=['name', 'lat', 'lng']) # 第2步:范围校验,经纬度写反时自动交换 mask = (df['lat'].between(-90, 90)) & (df['lng'].between(-180, 180)) # 这里用更严格的区间判断,超出合理城市范围的数据也可以手动抽查 df = df[mask] # 第3步:按名称与坐标去重 df = df.drop_duplicates(subset=['name', 'lat', 'lng']) # 第4步:构造GeoDataFrame,指定坐标系为WGS84 gdf = gpd.GeoDataFrame( df, geometry=gpd.points_from_xy(df['lng'], df['lat']), crs='EPSG:4326' ) # 输出GeoJSON,供QGIS、MapLibre等工具使用 gdf.to_file('poi_clean.geojson', driver='GeoJSON')

这一步做完,你的数据就已经有了一个非常扎实的起点。接下来进入设计环节,我会告诉你怎样让数据真正“好看”。

3. 地图设计:好质感来自克制

地图设计跟普通平面设计最大的区别是,信息必须准确优先,美感其次。一个我常说的比喻是:地图的视觉是给读者铺的红毯,红毯太花,人会摔跤;红毯太平,人会迷路。设计中最重要的不是技巧,而是做减法。

3.1 版式与视觉层级,先定骨架再谈细节

拿到数据后,不要急着调颜色,先确定版式骨架。如果是网页,考虑地图占屏多大、是否有侧边信息栏、是否有图例和比例尺。如果做打印小册子,先定页面的幅面,A4竖版、A4横版还是方形开本,这直接决定了地图上的最大可用尺寸。

我的经验是,信息密度越高的地图,周围留白越要多。地图集不是数据大屏,不适合把边角全部填满。每一页地图的标题、说明文字、图例、比例尺、指北针都应该有固定的位置,整套作品的风格统一性远大于单页的惊艳。你可以先画几张纸质草图,把每个区块的占位确定下来,再进入工具操作。这个习惯帮我避免过无数次“调一个下午最后推倒重来”的窘境。

3.2 专题符号与配色选择,宁可保守不要翻车

配色是地图质感的最大变量,也是新手最容易失控的地方。我一直推荐一套保守方案:底图用低饱和度的浅灰、浅米色,数据层用一两个饱和色,辅助信息用深灰。这样至少不会翻车。等审美积累够了,再尝试更大胆的撞色。

如果你是做分类数据,比如不同业态的咖啡店、不同类型的历史建筑,优先用色相去区分,而不是深浅。因为人眼对色相差异最敏感。如果是连续数据,比如评分、价格、销售额,那就用同一色相由浅到深的渐变色。把颜色数量和指标数量对应起来,不要一个指标用三个颜色。

关于符号,点数据的常见参数是半径大小、填充颜色、描边颜色、描边宽度。描边颜色和描边宽度经常被忽略,但它们对“精致感”影响巨大。一个白色细描边能让任何颜色的点从底图上浮起来,视觉上立刻专业很多。

3.3 标注排版与避让,细节决定是否耐看

文字标注是地图里最考验耐心的环节。地图上最难看的效果就是标注互相压盖、或标注压住了关键数据点。很多工具自带碰撞检测,但远不是万能的。我习惯在设置里调两个参数:一个是标注碰撞开关开启,另一个是让重复元素不重复显示,同一地点只出现一次文字。

另外一个很实用的小技巧是给文字加光晕。在QGIS和MapLibre里都可以配置,文字周围包一圈半透明的白色描边,底图再复杂,文字也能保持可读。字体选择上,中文字体的可选项比英文少得多,我习惯用思源黑体系列,干净且免费可商用。英文部分可以用无衬线字体,尽量避免在低分辨率屏幕上使用细体,细线文字放大后容易显得单薄。

还要注意地图上的“文字层级”,通常只给最重要的点标注名称,次要信息放图形里或者延迟到点击交互才显示。一屏之内如果超过二十个文字标注,读者的认知负担就会明显上升,不自觉地开始感到烦躁。

4. 实操:从零完成一份城市咖啡地图集

理论说了不少,现在走一遍完整流程。以“城市咖啡地图集”为例,目标产出一份交互网页,同时预留打印版输出。我假设你的原始数据已经放在一个CSV文件里,字段包括名称、地址、经度、纬度、评分和业态分类。

4.1 用QGIS把数据变成第一张图

QGIS是一款完全免费的开源GIS桌面软件,处理地图数据和快速样式验证非常顺手。打开QGIS之后,新建项目,先把坐标系设置为EPSG:4326,或者选择“开箱即用的项目模板”。然后把刚才清洗好的poi_clean.geojson直接拖入图层区域,QGIS会自动显示点数据。

接下来做最简单的样式验证:右键图层,进入属性,选择“分类”,按category字段分类,每个类别指定一个符号。你会立刻看到数据的大致分布。这一步的目的不是定稿,而是确认数据有没有明显问题,比如某个点飞到了城市外面、某个分类点数量意外极端。我建议在这个环节花5分钟做一次“目检”,比后面到网页上调试半天再发现数据错误要省时得多。

如果要做印刷版,QGIS的“布局管理器”足够生成一张完整的图幅,可以设置纸张尺寸、添加指北针、比例尺、图例。导出PDF之前把地图比例尺锁定,比如1:10000或1:20000,不要让每一页的比例尺乱跳,否则整套小册子的空间尺度感会崩塌。

4.2 用MapLibre做出可交互页面

进入网页交互阶段,我推荐MapLibre GL JS。它是一个开源地图引擎,不需要引入重型商业SDK,只需要一个HTML文件就能跑起来。核心思路是:先定义底图源,再定义数据源,最后把数据源渲染成图层。

一个最简示例是这样的,底图瓦片地址请替换成你信任且可用的服务地址:

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8" /> <title>咖啡地图集</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <script src="https://unpkg.com/maplibre-gl@3/dist/maplibre-gl.js"></script> <link href="https://unpkg.com/maplibre-gl@3/dist/maplibre-gl.css" rel="stylesheet" /> <style> body { margin: 0; } #map { width: 100vw; height: 100vh; } </style> </head> <body> <div id="map"></div> <script> const map = new maplibregl.Map({ container: 'map', style: { version: 8, sources: { baseTiles: { type: 'raster', tiles: ['https://your-tile-server.example.com/{z}/{x}/{y}.png'], tileSize: 256, attribution: '你的底图来源' }, cafes: { type: 'geojson', data: 'poi_clean.geojson' } }, layers: [ { id: 'base-layer', type: 'raster', source: 'baseTiles' }, { id: 'cafe-points', type: 'circle', source: 'cafes', filter: ['==', 'category', '咖啡'], paint: { 'circle-radius': [ 'interpolate', ['linear'], ['get', 'rating'], 3.0, 4, 5.0, 12 ], 'circle-color': '#a64d2e', 'circle-stroke-width': 1.5, 'circle-stroke-color': '#ffffff' } } ] }, center: [113.27, 23.13], zoom: 13 }); </script> </body> </html>

这个示例里我用评分字段动态控制了圆的半径,评分高的咖啡店看起来更大。interpolate表达式是MapLibre里非常好用的功能,作用是把一个连续值映射成另一个连续值,相当于给数据一个线性变换,比手动分成几个固定档位要自然得多。

如果你想实现点击弹出信息,只需要再加一段事件处理:

map.on('click', 'cafe-points', (e) => { const feature = e.features[0]; new maplibregl.Popup() .setLngLat(feature.geometry.coordinates) .setHTML('<b>' + feature.properties.name + '</b><br/>评分:' + feature.properties.rating) .addTo(map); });

当你的点数量特别大时,比如几万以上,直接把GeoJSON作为数据源会导致页面卡顿。解决办法是启动聚合,把临近的点合并成一个聚合点,用户放大后才会逐渐拆开。MapLibre的GeoJSON源原生支持聚合配置:

map.addSource('cafes-cluster', { type: 'geojson', data: 'poi_clean.geojson', cluster: true, clusterMaxZoom: 14, clusterRadius: 50 });

4.3 输出打印版小册子的思路

打印版的核心要求是“所见即所得”,千万不要用浏览器直接打印网页地图,因为网页地图的缩放级别和页面排版不可控。我的经验是,交互版用于探索,打印版单独在QGIS布局管理器里制作。

布局管理器的做法是:选择纸张尺寸,添加地图、图例、标题、比例尺。地图窗口里可放入多个已经调整好范围的视图,每个视图对应一页。每一页应当保持同一个比例尺,这样读者翻页的时候能直观感受到空间尺度的一致性。一个加分项是让每一页的视野略微覆盖上一页的边界,形成连续翻阅的过渡,就像真正的地图册一样。

导出PDF时,我会把背景图层的标注略微降一点透明度,既保留空间参照,又不跟专题信息抢注意力。最终PDF建议嵌入字体,这样别人打开时不会因为缺字体出现方块。

5. 常见问题与排查技巧实录

地图项目出问题是常态,不出问题才要怀疑自己是不是没用心。我把自己踩过的坑整理成了一份速查表,按现象、原因、解决办法三个维度列出来,方便你对照排查。

现象大概率原因解决思路
点全部出现在海洋或空白区域经纬度字段用反了,或混入了投影坐标检查字段范围和坐标系,统一为EPSG:4326
地图上只有一个点,其他点消失了GeoJSON里混入了空geometry或非法值清洗数据时做非空校验
点与底图错位几条街数据坐标系和底图坐标系不一致将数据重投影到EPSG:4326或EPSG:3857
中文标签显示为方块字体缺失,或渲染环境不支持中文字体指定系统字体或嵌入思源黑体
标注互相压盖严重没有开启碰撞检测或标注密度过高打开碰撞检测,启用重复元素去除,提高最小距离
地图加载非常卡顿数据量太大,直接加载GeoJSON启用聚合,或先用工具切瓦片
样式配置不生效图层顺序问题,或过滤器写法错误检查图层的source和filter语法,把底图图层放在最下面
导出PDF字体走样字体未嵌入在导出设置中勾选嵌入字体

5.1 现场排查的三个小技巧

除了表格里的常规解法,我再分享三个现场排查时特别好用的小技巧。第一个是“图层命名法”,在QGIS和MapLibre中,图层名称不要叫layer1、layer2,直接用像“咖啡店-点-评分渐变”这样的名字,调试时一眼就能定位到问题图层,项目兄妹接手也不会一头雾水。

第二个是“单层检查法”,遇到样式混乱时,把所有图层全隐藏,只打开该打开的图层,从底图开始依次往上加。当加某个图层出现问题的那一步停下,问题基本就定位了。地图是图层嵌套系统,很多问题的根源就出在某一层的透明度或过滤条件上。

第三个是“颜色打印测试法”,本地预览颜色没问题的图纸,打印出来后往往偏灰或者偏深,因为屏幕是发光体而纸张是反光体。重要作品一定要试打印一次,用实际成品检验对比度是否足够。

5.2 三个值得长期坚持的好习惯

最后说三个我在多次项目里沉淀下来的好习惯,虽然和具体技术无关,但长期受益。

第一个是每次项目建一个独立目录,原始数据、清洗脚本、样式快照、导出文件分开存放。地图项目改动频率极高,没有清晰的目录会让你在两周后连自己写了什么都找不到。第二个是每改一版样式,就导出一次静态图存到“版本记录”文件夹。这能让你有勇气去尝试新方向,反正可以随时退回旧版本。第三个是数据更新要留痕,在GeoJSON或CSV源文件里加一个updated_at字段,记录这条数据最后更新时间,地图集一旦面向公开场合,标注这些信息会显得很专业,也方便被读者质疑时快速响应。

我在实际做地图集项目时,最大的感受是:真正决定作品高度的不是渲染引擎多炫、也不是配色多复杂,而是你对内容的理解和克制。地图集的仪式感,来自它在信息洪流里替你做了筛选,替你安排了看世界的顺序。先想明白要讲什么,再去碰工具,这条路永远不会绕远。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询