内容提要
EveryCityMap通过OpenStreetMap数据管道构建城市地图:先收集完整城市数据并重建边界,填充海陆,再分割为预览、核心、细节增量及存档四个PBF块,经CDN和IndexedDB缓存,最终用Canvas2D渲染。系统确保数据一致性,支持渐进加载和主题切换,当前稳定版含535个城市。
延伸解读
数据一致性优先于便利性
文章强调,每个城市的所有数据块必须来自同一次采集的规范源,绝不混合不同时间或端点的数据。这种设计避免了因数据不一致导致的渲染错误,但也意味着更新不实时,需要离线或CI计算。对于需要精确地图数据的应用,这种取舍是值得的。
分块策略的权衡
将城市数据分为预览、核心、细节增量和存档四个块,是为了平衡下载成本和功能需求。核心块包含主要道路和土地覆盖,细节增量仅在用户选择详细模式时加载,存档块则保留未来可能用到的道路。这种设计让用户按需下载,但增加了数据管理的复杂性。
缓存与验证机制
浏览器通过IndexedDB缓存已验证的PBF块,缓存键包含版本、城市和哈希,确保不会混用旧数据。每次打开城市都会重新验证哈希和身份,损坏的块会被重新获取。这种机制保证了数据的完整性,但也意味着缓存容量有限(最多48块),需要合理管理。
渲染与导出的统一
Canvas2D渲染器使用固定的绘制顺序,主题变化只改变视觉属性,不重新获取数据。预览和导出使用相同的drawCityMap()函数,确保所见即所得。生产导出会明确报告失败,避免静默差异。这种设计保证了不同场景下的一致性。
Q&A
EveryCityMap如何获取城市数据?
EveryCityMap通过Overpass API一次性获取每个城市的完整数据,包括道路、铁路、水域、绿地、机场、港口、地形和边界拓扑,并记录查询哈希、文件哈希、源端点和OSM时间戳以确保数据一致性。
EveryCityMap为什么将城市数据分成四个PBF块?
为了优化加载性能,EveryCityMap将每个城市的数据分成四个PBF块:预览块(Preview)、核心块(Core)、细节增量块(Detail delta)和私有存档块(Private Archive)。这样浏览器可以按需加载,避免一次性下载全部数据,同时保证数据完整性和渐进加载。
EveryCityMap如何确保数据一致性?
EveryCityMap通过以下方式确保数据一致性:每个城市只从一个完整的Overpass响应构建,记录查询哈希和文件哈希;所有交付块必须来自同一个规范源;下载时验证SHA-256哈希、PBF类型和城市身份;缓存键包含版本和哈希,防止旧数据复用。
EveryCityMap的浏览器端如何实现渐进加载?
浏览器端通过资产计划获取短时有效的URL,先加载核心块(Core)即可显示标准模式,详细模式则同时请求核心和细节增量块,但核心块到达后即可渲染,细节增量块到达后合并。如果细节请求失败,仍保留核心地图并标记为部分结果。
EveryCityMap为什么选择Canvas2D渲染而不是MapLibre?
因为EveryCityMap需要统一渲染整个城市,支持主题切换、图层控制、预览和导出,Canvas2D可以复用相同的几何数据,通过Path2D缓存提高性能,并确保预览和导出使用相同的渲染逻辑。
EveryCityMap如何处理城市边界?
EveryCityMap使用OSM行政关系定义城市边界,但添加3公里自然缓冲区,使道路、水域和陆地可以延伸出政治边界,避免地图在边界处出现生硬截断。
EveryCityMap的发布流程是怎样的?
EveryCityMap采用不可变发布和通道指针分离的方式。每个候选版本必须通过所有验证(包括目录覆盖、修订、资产身份和存储读取)才能激活。激活时原子地移动稳定通道指针,旧版本保留作为回滚源。
EveryCityMap当前支持多少个城市?
截至2026年9月1日,稳定版v3包含535个城市,映射到559个OSM关系。未来计划扩展到959个城市,其中949个已收集,10个仍在处理中。