您在竞争对手应用程序上见过的每张地图都使用相同的默认样式。通用的米色道路、中等色调的蓝绿色水域、与您自己的 UI 争夺注意力的杂乱 POI 图标,这些都是通用默认值,设计成不让任何人厌恶,也不让任何人喜爱。它们清晰地传达了一件事:这个产品正在使用地图库,而不是拥有自己的地图体验。
自定义地图样式改变了这一切。物流仪表板上的暗色模式地图、房地产应用上品牌绿色的路网、带有您徽标的简洁极简主义样式(取代了曾经的署名水印),这些细节将一个工具提升为一个产品。
本教程介绍 MapAtlas 实现的 Mapbox Style Specification,从样式 JSON 的结构到 React 中完整的明/暗主题切换。您不需要 GIS 经验,只需要熟悉 JSON 和 JavaScript。
如果您还没有设置基础地图,请先阅读如何向网站添加交互式地图,然后再回来进行样式设置。
为什么默认地图样式看起来很通用
瓦片提供商的默认样式设计为在尽可能广泛的受众、尽可能广泛的使用场景中普遍可读。它优先考虑信息密度而非审美观点。道路需要可见,标签需要对比,POI 图标需要可识别。
这些目标与您产品的目标都不一致。您的应用程序可能只关心地图上一小部分内容,配送路线、房产位置、医疗机构,其余一切都是视觉噪音。
自定义样式让您可以抑制不需要的内容,强调需要的内容,并确保地图作为产品的一部分来阅读,而不是一个嵌入的第三方组件。
理解样式规范
MapAtlas 样式是一个有五个关键部分的 JSON 文档:
{
"version": 8,
"name": "My Brand Style",
"sources": { ... },
"glyphs": "https://cdn.mapmetrics-atlas.net/basemaps-assets/fonts/{fontstack}/{range}.pbf",
"sprite": "https://cdn.mapmetrics-atlas.net/basemaps-assets/sprites/v4/light",
"layers": [ ... ]
}
sources,地图数据来源(MapAtlas 矢量瓦片端点)glyphs,用于标签渲染的字体文件加载位置sprite,POI 标记图标图像的加载位置layers,有序的视觉图层列表,每个图层都有绘制和布局属性
layers 数组是您花费最多时间的地方。每个图层指定数据类型(fill、line、symbol、circle、fill-extrusion)、要绘制哪些来源要素,以及如何绘制它们。
以下是一个渲染矢量瓦片底图的最小但完整的样式:
{
"version": 8,
"name": "Brand Base",
"sources": {
"mapatlas": {
"type": "vector",
"tiles": ["https://gateway.mapmetrics-atlas.net/planet20251013/{z}/{x}/{y}.mvt?token=YOUR_API_KEY"],
"minzoom": 0,
"maxzoom": 14
}
},
"glyphs": "https://cdn.mapmetrics-atlas.net/basemaps-assets/fonts/{fontstack}/{range}.pbf",
"sprite": "https://cdn.mapmetrics-atlas.net/basemaps-assets/sprites/v4/light",
"layers": [
{
"id": "background",
"type": "background",
"paint": { "background-color": "#f5f0eb" }
}
]
}
这在您选择的背景颜色上渲染一个空白画布。从这里开始,您为水域、土地利用、道路、建筑物和标签添加图层。
暗色模式:更改核心颜色
暗色模式地图不只是简单地反转栅格图像,而是将完全不同的绘制属性应用于相同的矢量几何体。这是矢量瓦片存在的核心原因之一,一个瓦片,无限种视觉解释。
暗色主题需要更新的关键颜色:
| 图层 | 浅色模式 | 深色模式 |
|---|---|---|
| 背景(陆地) | #f5f0eb | #1a1a2e |
| 水域 | #a8d5e5 | #0d3b66 |
| 城市土地利用 | #e8e0d5 | #16213e |
| 公园/绿地 | #c8e6c9 | #1b4332 |
| 主要道路 | #ffffff | #3a3a5c |
| 次要道路 | #efefef | #2d2d4a |
| 道路标签 | #333333 | #cccccc |
以下是在样式图层中应用的方式:
{
"id": "water",
"type": "fill",
"source": "mapatlas",
"source-layer": "water",
"paint": {
"fill-color": "#0d3b66",
"fill-opacity": 1
}
}
{
"id": "road-major",
"type": "line",
"source": "mapatlas",
"source-layer": "transportation",
"filter": ["==", "class", "primary"],
"layout": {
"line-join": "round",
"line-cap": "round"
},
"paint": {
"line-color": "#3a3a5c",
"line-width": ["interpolate", ["linear"], ["zoom"], 10, 1, 16, 4]
}
}
line-width 中的 interpolate 表达式根据缩放级别缩放道路宽度,在低缩放时较窄,放大时更宽。这是无需任何额外网络请求的数据驱动样式设置。
应用品牌颜色
大多数设计系统定义了主色(例如 #97C70A)、辅助色和中性色调。地图应使用相同的值,而不是近似值。
针对品牌颜色应用的目标图层:
道路作为品牌元素。 如果您的品牌颜色足够鲜明,可以将其用于主干道或突出显示的路线:
{
"id": "road-highlighted",
"type": "line",
"source": "mapatlas",
"source-layer": "transportation",
"filter": ["==", "class", "primary"],
"paint": {
"line-color": "#97C70A",
"line-width": 3,
"line-opacity": 0.8
}
}
自定义 POI 标记。 通过托管自定义精灵图并更新 sprite URL,用您自己的图标替换默认精灵图标。然后在符号图层中引用您的图标:
{
"id": "brand-poi",
"type": "symbol",
"source": "mapatlas",
"source-layer": "poi",
"layout": {
"icon-image": "brand-pin",
"icon-size": 1.2,
"text-field": ["get", "name"],
"text-font": ["Inter Bold", "Noto Sans Regular"],
"text-size": 12,
"text-offset": [0, 1.5],
"text-anchor": "top"
},
"paint": {
"text-color": "#97C70A",
"text-halo-color": "#1a1a2e",
"text-halo-width": 1
}
}
地图标签的自定义字体
默认地图标签字体是通用的。如果您的品牌使用 Inter、Roboto 或自定义字体,您也可以将其用于地图标签。
MapAtlas 从自己的字形端点提供字形,支持标准 Mapbox 字形格式。要使用自定义字体:
- 使用
fontnik等工具从您的字体生成.pbf字形文件。 - 在 CDN 或自己的服务器上托管字形文件。
- 更新样式 JSON 中的
glyphs字段:
{
"glyphs": "https://your-cdn.com/fonts/{fontstack}/{range}.pbf"
}
- 在任何符号图层的
text-font属性中引用该字体:
"text-font": ["Inter Bold", "Noto Sans Regular"]
始终提供回退字体(Noto Sans 覆盖主字体可能不包含的 Unicode 字符)。渲染器使用数组中第一个包含每个字符字形的字体。
删除水印和署名
MapAtlas 允许完全的白标部署。您可以完全从地图中删除 MapAtlas 徽标,同时保留数据许可证要求的 OpenStreetMap 署名。
在 SDK 中,署名由一个您可以禁用的控件处理:
const map = new mapmetricsgl.Map({
container: 'map',
style: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/style.json&token=YOUR_API_KEY',
center: [4.9041, 52.3676],
zoom: 12,
attributionControl: false, // Remove the default attribution control
});
如果您想保留署名但将其样式调整为与您的 UI 匹配,请隐藏默认控件并添加自己的控件:
map.addControl(
new mapmetricsgl.AttributionControl({
customAttribution: 'Map data © OpenStreetMap contributors',
compact: true,
}),
'bottom-left'
);
这将品牌标识替换为最简化的、符合法律要求的署名,尊重 OpenStreetMap 的许可证,同时匹配您的 UI,因此地图如何呈现品牌完全由您掌控。
React 中的明/暗主题切换
以下是一个完整的 React 组件,管理带明/暗切换的地图,使用 CSS 自定义属性保持主题与 UI 其他部分同步:
import { useEffect, useRef, useState } from 'react';
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';
const STYLES = {
light: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/style.json&token=YOUR_API_KEY',
dark: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_DARK_STYLE_ID/style.json&token=YOUR_API_KEY',
};
export function BrandedMap({ center = [4.9041, 52.3676], zoom = 12 }) {
const containerRef = useRef(null);
const mapRef = useRef(null);
const [theme, setTheme] = useState('light');
useEffect(() => {
const map = new mapmetricsgl.Map({
container: containerRef.current,
style: STYLES.light,
center,
zoom,
attributionControl: false,
});
map.addControl(
new mapmetricsgl.AttributionControl({ compact: true }),
'bottom-left'
);
mapRef.current = map;
return () => map.remove();
}, []);
useEffect(() => {
if (mapRef.current) {
mapRef.current.setStyle(STYLES[theme]);
}
}, [theme]);
return (
<div style={{ position: 'relative' }}>
<div ref={containerRef} style={{ width: '100%', height: '500px' }} />
<button
onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}
style={{
position: 'absolute',
top: 12,
right: 12,
padding: '8px 16px',
background: theme === 'dark' ? '#ffffff' : '#1a1a2e',
color: theme === 'dark' ? '#1a1a2e' : '#ffffff',
border: 'none',
borderRadius: 6,
cursor: 'pointer',
zIndex: 1,
}}
>
{theme === 'light' ? 'Dark mode' : 'Light mode'}
</button>
</div>
);
}
setStyle 调用以新的配色方案重新渲染地图。已在缓存中的瓦片数据不会被重新获取,只有视觉解释发生变化。
托管自定义样式 JSON
对于完全控制,请将样式 JSON 作为静态文件托管在您的 CDN 或 S3 存储桶上。这让您可以:
- 将样式与应用程序代码一起进行版本控制。
- 无需重新部署应用程序即可更新视觉设计(只需更新托管的 JSON)。
- 为不同环境(预览与生产)使用不同样式。
在运行时加载托管的样式:
const map = new mapmetricsgl.Map({
container: 'map',
style: 'https://your-cdn.com/styles/brand-dark.json',
center: [4.9041, 52.3676],
zoom: 12,
});
唯一的限制是 CORS:样式 JSON 必须以 Access-Control-Allow-Origin: *(或您的特定域名)提供,以便浏览器可以跨域获取它。
样式开发工作流程
构建自定义样式最快的方法是迭代式的,实时查看更改而无需重写代码。
- 从 MapAtlas 基础样式 JSON 开始作为基础。在 MapMetrics 门户 的 Styles 中创建一个样式,复制其 Style URL,然后获取该 URL 以下载 JSON。
- 在编辑器中编辑 JSON,观察哪个图层影响哪个视觉元素。
- 用修改后的样式 JSON 重新加载地图查看结果。
- 稳定后,将样式 JSON 提交到您的代码库并在 CDN 上托管。
更深入的定制,请查看地图可视化与样式文档,了解完整的图层参考、绘制属性类型和表达式语法。
如果您来自 Mapbox 并想知道有什么变化,请参阅Mapbox vs. MapAtlas:哪个地图 API 适合您的欧盟项目?,了解功能和定价的并排比较。
关于为什么矢量瓦片使这一切成为可能的架构基础,矢量瓦片与栅格瓦片详细解释了渲染管道。
总结
自定义地图样式不是奢侈品,而是地图感觉像第三方组件和感觉像为您的产品量身定制之间的区别。使用 MapAtlas:
- 样式规范是与 Mapbox GL JS 工具兼容的标准 JSON。
- 暗色模式是样式 JSON 交换,而不是新的瓦片集或服务器配置。
- 品牌颜色通过绘制属性应用于道路、标签和 POI 图标。
- 自定义字体从您控制的任何字形端点加载。
- 水印和署名完全可配置,所有付费方案都支持白标。
- 整个样式可以在运行时切换,无需页面重新加载。
立即开始构建您的品牌地图。注册免费 MapAtlas API 密钥并加载基础样式 JSON 作为起点。第一个自定义样式大约需要 30 分钟才能调好,后续迭代只需几分钟。
常见问题
我可以从地图中删除 MapAtlas 水印吗?
可以。MapAtlas 允许完全白标地图:您可以完全移除 MapAtlas 徽标并用自己的品牌替换,只需保留开放许可证要求的一小段 OpenStreetMap 数据署名。
MapAtlas 的样式格式与 Mapbox GL JS 样式兼容吗?
兼容。MapAtlas 使用 Mapbox Style Specification,即 Mapbox GL JS 和 MapLibre GL 使用的相同 JSON 格式。任何现有的 Mapbox 样式 JSON 只需更新来源 URL 即可在 MapAtlas 上使用。
我可以在不重新加载页面的情况下在浅色和深色地图样式之间切换吗?
可以。使用新的样式 URL 或对象调用 map.setStyle()。地图会以新的视觉配置重新渲染,而不会重新加载已缓存的瓦片数据。与 CSS 自定义属性结合,可以创建无缝的主题感知地图组件。

