第07章 - 样式符号化与图例
GeoLibre 的符号化集中在右侧的 Style 面板:在 Layers 面板里选中哪一层,它就编辑哪一层。矢量与栅格各有自己的一套控件,但都写进同一种记录——LayerStyle。这个记录与渲染引擎无关,四种引擎(MapLibre、Mapbox、Cesium、ArcGIS)各自把它编译成自己的图层。
理解这一点很关键:你配置的不是 MapLibre 的 paint 属性,而是一份可序列化、可导出、可跨引擎的样式记录。它决定了三件事:样式能随工程保存、能导出成 SLD/QML/Mapbox GL style,也决定了在 Cesium 与 ArcGIS 上某些能力会缺席——因为它们没有 Style Specification 这一层。
本章按”基础符号 → 数据驱动 → 表达式 → 标注 → 弹窗 → 过滤 → 互通 → 栅格 → 图例”的顺序展开,最后给出常见样式问题排查表。
说明:本章基于 GeoLibre 3.4。控件名与参数名保留英文原文。素材来自
docs/user-guide/styling.md、packages/core/src/types.ts、packages/core/src/color-ramp.ts、packages/core/src/vector-color.ts、packages/core/src/expressions.ts、packages/map/src/sld-import.ts、packages/map/src/qml-import.ts、apps/geolibre-desktop/src/lib/auto-legend.ts。
7.1 样式体系总览
Style 面板按图层类型分成两半:矢量控件与栅格控件。
| 能力 | 矢量图层 | 栅格图层 |
|---|---|---|
| 基础符号 | 填充、描边、点、标记、图案填充、线装饰 | 无(整幅影像) |
| 颜色体系 | 单值、分级、分类、规则、表达式、比例符号、热图、聚合 | 单波段伪彩、RGB 合成、归一化差值指数 |
| 标注 | 字段/表达式、比例尺范围、去重、数据驱动覆盖 | 无 |
| 三维 | 拉伸(3D extrusion)、Z 值渲染 | 无 |
| 弹窗/提示 | 设计器 | 无 |
| 快速过滤 | 支持 | 无 |
| 图像调整 | 无 | 亮度、对比度、饱和度、色相旋转 |
| 图例 | 自动派生 | 色带与分类行 |
样式记录的入口是 LayerStyle(packages/core/src/types.ts),它同时被 Style 面板、Layer actions 的样式导出、Style Manager 预设、工程文件与 MCP/Python 授权工具读取。这就是为什么”面板上能调的东西”与”导出文件里有的东西”高度一致——它们本来就是同一份数据。
提示:样式记录是声明式的。表达式只允许 MapLibre 表达式(JSON 数组),不接受任意 JavaScript。因此从共享工程、URL 样式或 MCP 脚本恢复样式是安全的:最坏情况是编译失败,而不是执行任意代码。
7.2 基础符号
7.2.1 填充、描边与点
| 控件组 | 参数 | 说明 |
|---|---|---|
| Fill | 填充色、填充不透明度 | 多边形的面 |
| Stroke | 线色、线宽 | 线与多边形轮廓 |
strokeWidthUnit:pixels / meters |
像素是屏幕空间常量;米是地面距离,线宽随比例尺变化(对应 QGIS 的”地图单位”) | |
| Points | 圆半径 | 点图层的默认表现 |
| Use marker icon | 改用标记图标(见 7.2.2) | |
| Labels | 文字色、字号、光晕色、光晕宽 | 详细配置见 7.6 |
| 3D extrusion | 高度字段、高度缩放、基高、颜色、高级表达式 | 见 7.2.5 |
另外可以为样式设置最小/最大缩放(minZoom / maxZoom),让某个样式只在缩放区间内生效。
注意:
strokeWidthUnit = "meters"只影响线与多边形轮廓;点与圆的描边仍然是像素。这是刻意的——一个”直径 10 米的点”在不同缩放下会小到看不见。
7.2.2 标记图标
打开 Use marker icon 后,点不再画成圆,而是一个标记:
| 选项 | 值 |
|---|---|
内置形状(markerShape) |
circle、square、triangle、diamond、star、cross、pin |
自定义图片(markerShape = "custom") |
Upload image… 接受 SVG、PNG、JPEG、GIF |
颜色(markerColor) |
只对内置形状与 SVG 生效 |
尺寸(markerSize) |
像素 |
关键区别:位图按原样绘制,标记颜色与逐类颜色都不生效;SVG 会被光栅化成可重着色精灵,因此颜色可控。选图标时优先用 SVG,否则”分类着色”会失效。
内置形状与自定义 SVG 都画在精灵贴图上,导出 Mapbox GL 样式时通过 styleimagemissing 事件按需提供。
7.2.3 图案填充
多边形除了平涂,还能用图案填充(fillPattern):
| 值 | 说明 |
|---|---|
none |
平涂 |
hatch / cross-hatch |
单向/交叉斜线 |
horizontal / vertical |
横线/竖线 |
dots |
点阵 |
svg |
用 fillPatternSvg 里的原始 SVG 标记光栅化 |
图案颜色由 fillPatternColor 控制。图案是 GeoLibre 生成的可重着色精灵,因此在 MapLibre/Mapbox 上都能绘制;导出 GeoLibre URL style 时,内置图案与其颜色、以及自定义 SVG 图案都会被保留(见 7.9.2)。
7.2.4 线装饰与几何生成器
这两个是 QGIS 式的”符号图层”能力,用来在不复制数据的前提下加信息。
线装饰(lineDecoration) 沿线重复绘制符号:
| 值 | 效果 |
|---|---|
none |
关闭 |
arrow |
跟随线方向转向的箭头 |
triangle / circle / square |
重复标记 |
配套参数:lineDecorationColor(留空继承描边色)、lineDecorationSize(像素)、lineDecorationSpacing(像素)。
几何生成器(geometryGenerator) 在要素的正常符号之上,再画一层派生几何:
| 值 | 派生几何 |
|---|---|
none |
关闭 |
centroid |
质心(点) |
bounding-box |
包围盒(面) |
convex-hull |
凸包(面) |
buffer |
缓冲区(面) |
缓冲区距离可以固定(geometryGeneratorBufferDistance,单位米),也可以由字段驱动(geometryGeneratorBufferProperty),字段值缺失或非数值时回退到固定距离——相当于 QGIS 的数据定义覆盖。
反向填充(invertedFillEnabled) 是第三种技巧:要素外部被填充、要素本身变成洞,用于”突出研究区之外”这类表达。它只对多边形生效,且在拉伸开启时被忽略。
7.2.5 三维拉伸与 Z 值
3D extrusion 把多边形变成块体:
| 参数 | 含义 |
|---|---|
extrusionEnabled |
开关 |
extrusionHeightProperty |
高度字段 |
extrusionHeightScale |
高度缩放 |
extrusionBase |
基高 |
extrusionColor / extrusionOpacity |
颜色与不透明度 |
extrusionColorExpression / extrusionHeightExpression |
高级表达式(颜色与高度都可数据驱动) |
分类、分级、表达式算出的颜色与高度都会如实反映到地图与三维模型导出中(见第 6 章 6.2.3)。
Z 值渲染(elevation3dEnabled) 是另一条正交的路径:坐标自带 Z 的矢量图层(例如带高程的 GPX 轨迹)通过共享 deck.gl 叠加层按真实海拔绘制,而不是压平在平面上。参数是 elevation3dVerticalScale(垂直夸张)与 elevation3dOffset(海拔偏移,米)。
注意:拉伸与 Z 值渲染是两件不同的事,同一图层只应开启其中一个。拉伸是把平面多边形按属性抬高;Z 值渲染是让每个顶点落在它自己的海拔上。
7.2.6 样式字段速查
| 面板上的概念 | 记录字段 | 取值 |
|---|---|---|
| 样式类型 | vectorStyleMode |
single / graduated / categorized / rule-based / expression |
| 分级字段 | vectorStyleProperty |
字段名 |
| 级数 | vectorStyleClassCount |
分级 2–12;分类 1–12 |
| 色带 | vectorStyleColorRamp |
见 7.4.7 |
| 分级方法 | vectorStyleClassificationScheme |
equal-interval / quantile / natural-breaks / standard-deviation / geometric-interval;手改后为 manual |
| 断点 | vectorStyleStops |
{ value, color, label } 数组 |
| 表达式 | vectorStyleExpression |
MapLibre 表达式 JSON 字符串 |
| 规则 | vectorRules |
VectorRule[](见 7.4.4) |
| 比例符号 | proportionalSizeEnabled 等 6 个字段 |
见 7.4.6 |
| 点渲染器 | pointRenderer |
single / heatmap / cluster |
| 图表 | diagramType + diagramFields |
见 7.4.8 |
| 弹窗 | popup(LayerPopupConfig) |
见 7.7 |
| 快速过滤 | quickFilters(LayerQuickFilter[]) |
见 7.8 |
7.3 点渲染器:热图与聚合
对纯点几何的 GeoJSON 图层(拖进来的、工具产出的、或用 Add Vector Layer 的 geojson 模式加载的),Style 面板会多出一个 Point renderer 控件:
| 渲染器 | 说明 | 参数 |
|---|---|---|
| Single symbol | 每个点一个圆,默认 | —— |
| Heatmap | 冷到热的密度面 | Heatmap radius(核大小,像素)、Heatmap intensity |
| Clustered | 邻近点聚成带计数的气泡,放大后散开 | Cluster radius(像素)、Cluster max zoom(超过该缩放不再聚合) |
未聚合的点沿用图层的圆样式。渲染器选择随工程保存。
热图还有两个字段级参数:heatmapColorRamp(内置色带)与 heatmapWeightProperty(权重字段,留空则每个点权重相同)。
聚合与过滤器的交互有一条必须记住的限制,而且它是双向的:
- MapLibre 在 source 阶段构建聚合,早于渲染器求值任何过滤。
- 持久化表达式过滤器与快速过滤器作用在聚合前的源数据上,因此气泡与其计数会跟随它们变化。
- 时间轴窗口、规则渲染的可见性过滤、嵌入页
setFilter是逐要素的渲染过滤,聚合开启时气泡计数仍是未收窄的数据。
要看到过滤后的真实分布,把点渲染器切回 Single symbol,此时每个点各画一个圆,没有聚合计数可误读。
提示:热图与聚合都只在点图层上出现。线、面的图层在 Style 面板里看不到 Point renderer 这一节。
7.4 数据驱动样式
7.4.1 五种 Style type
| Style type | 用途 | 关键输入 |
|---|---|---|
| Single symbology | 全要素同一符号 | 填充/描边/点 |
| Graduated | 数值字段分级着色 | 字段、级数、分级方法、色带 |
| Categorized | 每个不同值一个颜色 | 字段、级数、取值顺序、色带 |
| Rule-based | 规则列表,每条带自己的过滤与符号 | 规则数组 |
| Advanced expression | 直接写 MapLibre 表达式 | 表达式 JSON |
分级与分类共享四个控件,随后列出生成的断点,允许你先微调某一级的颜色再应用:
| 控件 | 分级 | 分类 |
|---|---|---|
| Attribute | 必填 | 必填 |
| Classes | 2–12 | 1–12,另有 All (n) 让每个不同值各占一色 |
| Scheme | Equal interval、Quantile、Natural breaks、Standard deviation、Geometric interval、Manual | Most frequent、Alphabetical、First values |
| Colormap | 见 7.4.7 | 见 7.4.7 |
点击 Apply style type 之前,地图不会变化。 这是有意设计的:你可以先反复调整分级与色带,看断点列表如何更新,直到满意再落图。
7.4.2 分级方法的数学含义
断点不是”色带的边缘”,而是每级的下界:N 个断点升序排列时,第 i 级覆盖 [stops[i], stops[i+1]),最后一级向上开区间。图例里的 ≥ value 与 QML/SLD 导出器都按这个约定读取。
| 方法 | 断点怎么算 | 适用与边界 |
|---|---|---|
| Equal interval | 在最小最大值之间等分 | 分布均匀时最直观;偏态数据会把绝大多数要素塞进第一级 |
| Quantile | 按分位点取,每级要素数相近 | 突出排序而非数值差距;相同值多时断点会重复 |
| Natural breaks | 最小化级内方差(Jenks) | 兼顾分布与可读性,计算最重 |
| Standard deviation | 以均值为中心、每级一个标准差宽 | 表达”离平均有多远”,配发散色带最佳 |
| Geometric interval | 从最小正值到最大值按等比增长 | 跨数量级的偏态数据(人口、收入、浓度) |
| Manual | 你手改的断点 | 当你编辑某一级的值、增删级时自动切换到此方法 |
两条实现细节会影响你看到的级数:
- 断点必须严格递增。偏态样本会让多个分位点落到同一个值,重复断点被合并,于是你可能只得到比请求更少的级数。MapLibre 会拒绝非严格递增的
step表达式,所以这个去重不是装饰而是必需。 - 标准差分级会丢掉越界断点。级数以均值为中心,级数为偶数时有一个断点正好落在均值上,为奇数时中间一级横跨均值;落在
(min, max]之外的内部断点被丢弃,让首尾的开区间吸收长尾,从而不产生空级。标准差用的是总体标准差(与 QGIS、ArcGIS 一致)。
几何间隔分级还有两个边界:等比数列不能从 0 或负数开始,所以它从最小正值起步,第一个断点仍是样本最小值(0 与负数都归入第一级);若样本里没有任何正值,则回退为等间隔分级。
Manual 的语义值得单独说明:只要你编辑了某一级的数值、增了一级或删了一级,方法就会自动变成 Manual,这样界面显示的方法名永远不会声称产出了一组其实是你手打的断点。反过来,改变级数、色带或属性都会按等间隔重新生成断点。
图例会在分级图层的字段名下标注方法,例如 “Quantile, 5 classes”,让导出或分享后的地图读者知道分级是怎么来的。在 Manual 出现之前保存的工程会继续显示当时的分级方法,即使断点后来被手改过——重新应用一次分级,或手动选 Manual,就能修正这个说明。
7.4.3 分类方法的取值顺序
| Scheme | 取哪些值、按什么顺序 |
|---|---|
| Most frequent | 出现次数最多的前 N 个值 |
| Alphabetical | 按值排序 |
| First values | 数据中首次出现的 N 个值 |
| All (n) | 每个不同值各占一色(受 12 级上限约束) |
7.4.4 规则渲染
规则渲染是 QGIS/ArcGIS 规则渲染的对应物。每条规则的结构(VectorRule):
export interface VectorRule {
id: string;
label: string;
filter: string; // MapLibre 布尔表达式,序列化成 JSON
color: string; // 6 位十六进制
isElse: boolean; // 兜底规则,无 filter
enabled?: boolean;
minZoom?: number;
maxZoom?: number;
parentId?: string; // 嵌套:子规则继承祖先的过滤与缩放范围
strokeColor?: string;
strokeWidth?: number;
fillOpacity?: number;
circleRadius?: number;
}
行为约定:
- 自上而下求值,第一个匹配的规则获胜;
isElse规则没有过滤,承接其余要素。 - 每条规则有自己的符号属性(描边色、线宽、填充不透明度、圆半径),未设置的项继承图层值。
- 每条规则有自己的缩放范围(
minZoom/maxZoom),对应 QGIS 的”比例尺范围”。 - 规则可以嵌套:子规则只匹配同时满足所有祖先过滤的要素,并继承祖先缩放范围与启用状态的交集。带子规则的规则是”组”,自身符号不绘制,只有叶子规则出图。
enabled: false只是暂时停用(组会连带停用整棵子树),不删除。
规则渲染编译成一条 MapLibre case 颜色表达式,兜底规则排在最后。
7.4.5 表达式渲染
Advanced expression 直接把一份 MapLibre 表达式交给样式,获得完全控制。最常用的原子是 case、match、step、interpolate:
["case",
["==", ["get", "landuse"], "residential"], "#fde68a",
["==", ["get", "landuse"], "industrial"], "#cbd5e1",
"#f8fafc"]
["interpolate", ["linear"], ["get", "pop"],
0, ["to-color", "#eff6ff"],
50000, ["to-color", "#2563eb"],
500000, ["to-color", "#1e3a8a"]]
表达式的校验走 MapLibre 的样式规范编译器,因此算符写错、参数个数不对、类型不匹配都会在对话框里报出来,而不是等地图上什么都不显示。
7.4.6 比例符号
比例符号让圆的半径或线宽由一个数值字段决定(QGIS 的”分级 → 大小”):
| 字段 | 含义 |
|---|---|
proportionalSizeEnabled |
开关 |
proportionalSizeProperty |
驱动字段 |
proportionalSizeMinValue / MaxValue |
字段的两端 |
proportionalSizeMinRadius / MaxRadius |
对应半径的两端 |
它与颜色分级是正交的:可以只按大小分、只按颜色分,或两者同时。
注意:
proportionalSizeProperty同时驱动线宽,所以对一个多边形图层开比例符号,轮廓线也会跟着变粗。如果只想让质心点带比例,用几何生成器的geometryGeneratorSize*系列字段,而不是这个。
7.4.7 内置色带
packages/core/src/color-ramp.ts 定义 20 条色带,名称与 @developmentseed/deck.gl-raster 的命名色图一一对应,因此同一个选择在矢量与栅格路径上含义一致:
| value | 界面名 | 类型 |
|---|---|---|
viridis |
Viridis | 连续、感知均匀、色盲友好 |
plasma |
Plasma | 连续 |
inferno |
Inferno | 连续 |
magma |
Magma | 连续 |
cividis |
Cividis | 连续、色盲友好 |
turbo |
Turbo | 连续、高对比 |
spectral |
Spectral | 发散 |
blues |
Blues | 单色序列 |
greens |
Greens | 单色序列 |
oranges |
Oranges | 单色序列 |
reds |
Reds | 单色序列 |
purples |
Purples | 单色序列 |
terrain |
Terrain | 地形 |
rdylgn |
Red-Yellow-Green | 发散 |
rdylbu |
Red-Yellow-Blue | 发散 |
rdbu |
Red-Blue | 发散 |
coolwarm |
Cool-Warm | 发散 |
jet |
Jet | 彩虹(不推荐用于定量表达) |
greys |
Greys | 单色序列 |
gray |
Grayscale | 灰阶 |
采样是线性插值:给 N 个级数,就在色带的锚点色之间均分取出 N 个十六进制颜色。因此级数变化时颜色会重新分布,而不是”多加一级就多一种颜色”。
选色带的经验规则:
- 有中心含义的数据(距均值偏差、增减率)用发散色带,并让中心色对应中性值;
- 单向的数量(人口、产量)用单色序列,读者能自然读出大小;
- 多类别等权用分类渲染,不要用彩虹色带暗示不存在的顺序。
7.4.8 图表符号
Diagram 控件在每个要素上画一个小图表,可以叠加在正常符号之上:
| 类型 | 说明 |
|---|---|
| Pie chart | 饼图 |
| Donut chart | 环图 |
| Bar chart | 条形图 |
| Stacked bar chart | 堆叠条形图 |
字段来自你挑选的数值列(diagramFields),每列一个颜色。尺寸有三种模式:fixed(固定像素)、sum(按各列之和的平方根缩放,面积真实)、attribute(按指定字段缩放)。另有 diagramMinZoom(低于该缩放不画,避免糊成一片)与 diagramDeclutter(按面积从大到小放置,跳过会造成重叠的图表)。
图表画在要素点位置或多边形质心上,通过共享 deck.gl 叠加层渲染。它是表达”构成”的标准手段:得票份额、土地利用组合、年龄结构。
7.5 表达式构建器
规则过滤、表达式样式、标注、弹窗标题与正文、按表达式选择、虚拟字段、字段计算器、图集——这些入口共用同一个表达式构建器,其逻辑在 packages/core/src/expressions.ts(注释里写作 GH #1306)。把它放在 core 而不是桌面应用里,就是为了让每个入口共用一份实现。
7.5.1 函数参考
构建器内置一份精选函数表,按类别分组,每条都给出可直接编译的片段:
| 类别 | 包含的算符/函数 |
|---|---|
| data | get、has、id、properties、geometry-type、at、in、index-of、length、literal、typeof |
| decision | case、match、coalesce、==、!=、>、>=、<、<=、all、any、! |
| math | +、-、*、/、%、^、abs、ceil、floor、round、sqrt、ln、log10、min、max |
| string | concat、upcase、downcase、slice、number-format |
| conversion | to-number、to-string、to-boolean、to-color |
| camera | zoom、interpolate、step、rgb、rgba |
这份列表是”精选”而非”全集”,但它覆盖了符号化里绝大多数写法。插入片段始终是一个可编译的起点,有测试守着这一点。
7.5.2 @ 变量
构建器支持 @ 形式的变量占位,在编译前被替换成当前值:
| 变量 | 值 |
|---|---|
@map_zoom |
当前缩放 |
@map_scale |
当前比例尺分母 |
@feature_count |
图层要素数 |
@project_name |
工程名 |
@layer_name |
图层名 |
替换规则(substituteExpressionVariables):只替换整串恰好等于已知标记的字符串节点,未知的 @ 开头字符串原样保留,因此一个恰好以 @ 开头的属性值不会被误改。
警告:
@变量是一次性替换,不是动态绑定。用作持久化过滤、图层样式或分享出去时,它们已经被当时的数值替换掉。需要随相机变化的样式,请写["zoom"]或用interpolate/step包住。
7.5.3 校验与预览
校验用 MapLibre 样式规范的 createExpression,失败分类有四档:
| 错误码 | 含义 |
|---|---|
not-json |
不是合法 JSON |
not-array |
不是数组 |
not-operator |
数组首项不是字符串(不是算符) |
compile |
样式规范拒绝(参数个数、未知算符、类型不匹配) |
两个实用细节:
- 容忍尾逗号。手写的 JSON 常留尾逗号,
removeTrailingJsonCommas会在字符串之外去掉它们,所以["get","pop",]也能通过。 - 可指定期望类型。传
expectedType会让样式规范额外检查表达式结果类型,因此一个能编译但产生颜色的表达式不会被误用作过滤条件。
export type ExpressionExpectedType = "boolean" | "color" | "string" | "number";
export interface ExpressionValidation {
ok: boolean;
code?: "not-json" | "not-array" | "not-operator" | "compile";
errors: string[];
parsed?: unknown[];
}
预览会把表达式对一条样例要素求值,直接显示结果或错误。这条路径与校验共用同一次编译,不会重复编译同一份源码。
7.6 标注
标注由 LabelStyle 描述,编译成一条 MapLibre symbol 图层。
7.6.1 基本项
| 项 | 字段 | 说明 |
|---|---|---|
| 开关 | enabled |
—— |
| 字段 | field |
标签文本取自该属性 |
| 表达式 | expression |
非空时覆盖 field,例如拼接多个字段 |
| 位置 | placement |
point(要素/质心)或 line(沿线) |
| 字号 | size |
像素 |
| 文字色 | color |
CSS 颜色 |
| 光晕 | haloColor / haloWidth |
提高复杂底图上的可读性 |
| 缩放范围 | minZoom / maxZoom |
0/24 表示继承图层范围 |
| 允许重叠 | allowOverlap |
关闭时 MapLibre 隐藏碰撞的标注 |
| 锚点 | anchor |
center/left/right/top/bottom 与四个角 |
| 偏移 | offsetX / offsetY |
单位 em,Y 正值向下 |
| 旋转 | rotation |
顺时针角度 |
| 最大宽度 | maxWidth |
em,超出换行 |
| 大小写 | transform |
none / uppercase / lowercase |
7.6.2 数字格式
数值字段常常是 1234567.5 这样连成一片的字符串。开启 numberFormatEnabled 后,标注按区域格式输出:1,234,567.5。
| 字段 | 说明 |
|---|---|
numberFormatEnabled |
开关 |
numberDecimals |
保留小数位,0–10 |
numberLocale |
BCP 47 标签,空则跟随应用语言 |
它只作用于 field,不作用于 expression——表达式自己用 number-format 算符格式化。
7.6.3 去重
同一位置堆叠多个点时,标注会叠在一起糊掉。dedupe 有三个值:
| 值 | 行为 |
|---|---|
off |
每个要素都标(历史行为) |
unique |
同一位置的要素合并成一个标注 |
concatenate |
同一位置的要素合并成一个标注,把各自不同的字段值逐行拼起来 |
它只对通过内联 GeoJSON 路径渲染的点图层生效,且用 field 而不是 expression 取标签值。
注意:去重与”数据驱动覆盖”互斥。合并后的聚合要素只携带标签值本身,因此
sizeExpression、colorExpression、opacityExpression、visibilityExpression、priorityExpression在去重激活时会被跳过。若既需要按属性上色又想合并标注,只能二选一。
7.6.4 数据驱动覆盖
五个表达式字段让标注逐要素变化:
| 字段 | 结果类型 | 用途 |
|---|---|---|
sizeExpression |
number | 按人口等数值放大字号 |
colorExpression |
color | 按类别给字上色 |
opacityExpression |
number 0..1 | 替换图层级透明度(不会破坏顶层 ["zoom"] 插值) |
visibilityExpression |
boolean | 逐要素决定标不标(例如低于阈值不标) |
priorityExpression |
number | 作为 symbol-sort-key,小值优先放置,空间紧张时先赢 |
7.6.5 不同引擎的标注差异
标注是最能体现”样式记录 + 引擎编译”这个模型的例子:
| 能力 | MapLibre / Mapbox | Cesium | ArcGIS |
|---|---|---|---|
| 字段/表达式文本 | 有 | 有 | 有 |
| 字号、颜色、光晕 | 有 | 有 | 有 |
| 锚点与偏移 | 有 | 有 | 有 |
| 沿线放置 | 有 | 无(线标注落在中点) | 有 |
| 旋转 | 有 | 无 | 有 |
| 碰撞避让 | 有 | 无(始终允许重叠) | 有 |
| 优先级表达式 | 有 | 无 | 无 |
| 数字格式 | 有 | 有 | 有 |
ArcGIS 的标签符号不是数据驱动的,所以引擎把逐要素求值的结果按字号与颜色分组,每组一个标签类——连续色带会被分箱成几十个类。Cesium 的标注不带旋转与碰撞处理,allowOverlap 在地球上恒为真。
7.6.6 标注排错
| 现象 | 检查 |
|---|---|
| 完全看不到标注 | enabled 是否开;缩放是否在范围内;字段是否被隐藏 |
| 只显示一部分 | 碰撞避让在起作用——开 allowOverlap 或提高 priorityExpression |
| 文字互相压住 | 用 dedupe,或减小字号、加 maxWidth |
| 数字挤成一团 | 开 numberFormatEnabled 并设小数位 |
| 光晕难看 | 光晕宽度设为字号的 1/8 到 1/5,颜色取底色的反相 |
| 三维球面上标注穿地 | 引擎的地平线剔除,不是样式问题(地球背面的点与标注会被隐藏) |
7.7 弹窗与悬停提示设计器
Identify 默认把所有列一股脑倒出来。对分享出去的地图,这很糟:连接产生的中间列、编辑追踪列、内部 id 都会出现在读者眼前。Style 面板下方的 Popup 一节就是为此存在的。
7.7.1 两个独立开关
| 开关 | 默认 | 说明 |
|---|---|---|
| Show popup on click (Identify) | 开 | 点击要素时的弹窗 |
| Show tooltip on hover | 关 | 悬停时的小提示 |
7.7.2 标题
| 选项 | 行为 |
|---|---|
| Title field | 用要素自己的名称字段做标题,替代图层名 |
| Title expression | 用表达式生成标题,优先于字段 |
表达式失败或产出空串时会回退到 titleField,再回退到图层名——这条回退链保证标题永不为空。
7.7.3 正文
| 选项 | 行为 |
|---|---|
| Body expression | 用一句由属性拼成的话替换整个正文,连字段行与要素 id 行一起替换 |
| Fields | 选择显示哪些属性、以什么顺序显示 |
Fields 的编辑方式:从选择器添加字段,拖动手柄(或用箭头按钮)排序,给每个字段一个 Label 替代原始列名。留空(或把加进来的字段全部移除)则保持默认:所有可见字段、按数据自身顺序。
7.7.4 值的格式化
每个字段都有 Value type:
| 类型 | 可选项 |
|---|---|
| Number | 小数位、千分位 |
| Date | date、date and time、time、ISO 8601、year |
| Link | 链接文本(默认取字段值本身) |
| Image | 缩略图 |
| Text | 纯文本 |
Text、Number、Date 还支持 Prefix / Suffix——货币符号与单位就放这里。
In hover tooltip 把字段放进悬停提示的短子集。规则很简单:悬停提示跟着指针走,是”一眼”而不是一张表,所以只放一到两个字段。图片字段会被跳过,只在点击弹窗里显示,因为那里才有空间。
7.7.5 记录结构与边界
LayerPopupConfig 的完整字段比面板显示的更多:
export interface LayerPopupConfig {
click?: boolean; // 默认 true
hover?: boolean; // 默认 false
titleField?: string;
titleExpression?: string; // 优先于 titleField
bodyExpression?: string; // 替换整个正文
showFeatureId?: boolean; // 默认 true;false 去掉合成 id 行
maxWidth?: number; // 默认 520px(带图片时 420px)
imageHeight?: number; // 默认 min(50vh, 420px)
fields?: PopupFieldConfig[];
}
三条边界必须知道:
- 隐藏字段不会被重新暴露。属性表里被标为 hidden 或 excluded 的字段,弹窗设计器不提供,即使直接写进配置也会被剔除。
fieldVisibility始终是权威。 - 面板保留的图层没有设计器。通过 Add Vector Layer 加载、但仍是瓦片化/流式/超限的图层,GeoLibre 里没有要素可读,因此 Popup 一节不出现(移交规则见第05章 数据添加与格式支持 5.2.8)。面板自己的 Popup 复选框打开的是它自带的原始属性表,默认关闭——两者不要同时开。
- 设计随工程保存,也会传到 Python 包与 MCP 授权工具里,因此脚本生成的图层可以用同一条记录定义弹窗。
7.8 快速过滤器
Quick filters 一节让你不写表达式就收窄图层显示。点 Add a filter… 选一个字段,GeoLibre 会读数据决定该用哪种控件:
| 控件 | 何时出现 | 交互 |
|---|---|---|
| Values | 分类/文本字段 | 复选框列表,每个值旁带计数;值多时出现搜索框 |
| Range | 数值字段 | 滑块 + 可输入的最小/最大值 |
| Dates | 时间戳字段 | from/to 一对日期,两端当天都完整包含 |
| Text | 文本字段 | contains、starts with、is exactly 三种匹配,忽略大小写 |
字段旁的 Filter type 下拉可以在该字段支持的控件间切换——一个以数字编码的列默认开成 Range,你可以改成 Values 用复选框选。
多条过滤在同图层上叠加收窄,并与时间轴窗口、嵌入页 setFilter、规则渲染的可见性过滤共同作用,而不是替代它们。
三条行为边界:
- 快速过滤是隐藏,不是选择。 要选择要素用 Select by Expression(它可以只做一次选择,也可以应用为持久化表达式过滤)。
- 过滤器是持久化的”控件状态”,不是编译后的表达式。
quick-filters.ts在同步时把它编译成 MapLibre 过滤,因此保存的过滤总能被重新打开并继续编辑。 - 瓦片支撑的图层没有本地要素(矢量瓦片、PMTiles、MBTiles),它们的值列表来自当前已加载的瓦片,会随着平移缩放增长,面板在控件下方会说明这一点。
一个反直觉的细节:清空 Values 列表里最后一个勾选,等价于清空该过滤——显示全部值,而不是一个都不显示。要彻底删除控件用 Remove all;要清空全部条件但保留控件,用 Layer actions → Clear filters。
有活动过滤的图层行上会出现漏斗图标(见第 6 章 6.2.2)。
7.9 样式互通:SLD、QML、Mapbox GL 与 GeoLibre URL style
Layer actions → Styles 子菜单是样式互通的入口(菜单位置见第06章 图层管理与地图控制 6.1.2)。
7.9.1 四种格式的分工
| 格式 | 方向 | 是否含数据 | 适用场景 |
|---|---|---|---|
GeoLibre URL style(.geolibre.style.json) |
导入 + 导出 | 不含要素数据 | 配合 ?data= 发布单图层深链 |
| Mapbox GL style | 导入 + 导出 | 导出时内嵌该图层 GeoJSON | 交给 MapLibre/Mapbox 生态渲染 |
| OGC SLD | 导入 + 导出 | 不含 | 交给 GeoServer、桌面 GIS 或 OGC 工作流 |
| QGIS QML | 导入 + 导出 | 不含 | 与 QGIS 双向搬运符号化 |
两个入口细节:
- Import style from file (GeoLibre URL / Mapbox GL / SLD / QML)… 会按内容判断格式,不看扩展名。改错后缀的文件照样能认出来。
- Import style from text… 打开一个粘贴框,用于从 QGIS、gist 或目录页复制的样式。它读同样四种格式,判断依据是文本本身,报同样的警告。Style 面板标题栏里有同一个入口,不必离开面板。
提示:如果要一次改很多图层,用 Project → Export → Export Layer Styles…(按图层名写出全部样式)与 Project → Import → Import Layer Styles…(按名字匹配应用)。
7.9.2 GeoLibre URL style
这是为 URL 深链设计的紧凑格式:
- 不含任何要素数据,只有渲染图层与样式;
- 其 MapLibre 渲染图层用原始 GeoJSON 文件名主干作为
source,因此一份样式文档能区分 ZIP 里的多个 GeoJSON 成员; - 保留内置多边形填充图案及其颜色、以及自定义 SVG 填充图案:内联 SVG 标记与 SVG data URL 存在样式文件里,HTTP(S) 的 SVG URL 仍是外部引用,加载时必须可访问;
- 每个填充渲染图层的
metadata里带一条版本化的geolibre:fill-pattern记录,让图案在多文件 ZIP 合并样式时仍与正确的 source 关联;URL 加载与样式导入两条路径都会恢复这个扩展; - 其他 MapLibre/Mapbox 客户端会忽略这个扩展,退回平涂;
- 自定义点标记不会被保留——它需要生成的精灵图。
用法示例:
https://web.geolibre.app/?data=https://assets.geolibre.app/data/places.geojson&style=https://assets.geolibre.app/data/sample.style.json
交互式导入 GeoLibre URL style 时,基于文件名的 source 关联会被忽略,因为当前选中的图层就是目标。
7.9.3 SLD 导入
SLD 解析器(packages/map/src/sld-import.ts)返回一个补丁而不是完整样式:
export interface SldImportResult {
style: Partial<Omit<LayerStyle, "labels">>; // 只有能确定的键
labels: Partial<LabelStyle> | null; // 无 TextSymbolizer 时为 null
warnings: string[]; // 无法精确表达的部分
matchedRuleCount: number; // 理解的 SLD 规则数
}
这种设计的好处是”合并而非覆盖”:补丁叠在图层现有样式之上,没提到的属性保持原值。warnings 记录无法翻译的过滤、非平涂的符号化、混合的渲染器形态——导入不会静默丢掉符号化。
SLD 与地图是两套尺度坐标:SLD 用比例尺分母,地图用缩放级别。两者通过 sld-export.ts 里的 OGC_SCALE_DENOMINATOR_AT_ZOOM_0 换算,因此 SLD 的 MinScaleDenominator / MaxScaleDenominator 能映射成图层缩放范围与规则的 minZoom / maxZoom。
7.9.4 QML 导入
QML 解析器(qml-import.ts)与 SLD 解析器结构对称,结果形状相同。它把 QGIS 的符号类型映射回 GeoLibre 的形状枚举:
const QGIS_NAME_TO_SHAPE: Record<string, MarkerShape> = {
square: "square",
triangle: "triangle",
star: "star",
cross: "cross",
diamond: "diamond",
};
不在这张表里的 QGIS 标记名会落回默认形状并记入 warnings。
7.9.5 会丢失什么
| 设置 | 导出到 SLD/QML/样式文件 | 原因 |
|---|---|---|
| 混合模式(Blend) | 丢失 | MapLibre/Mapbox 样式规范里没有对应项 |
| 自定义点标记 | 丢失(导出 Mapbox GL 样式时) | 需要生成的精灵贴图 |
| 图表符号 | 视目标格式 | 目标格式没有对应概念 |
| 三维拉伸 | 视目标格式 | 同上 |
| 弹窗/悬停设计 | 不随样式走 | 属于图层记录,不属于符号化 |
| 快速过滤 | 不随样式走 | 同上 |
警告:把样式导出去再导回来,不是无损往返。跨格式搬运时,只把”目标格式表达得了”的部分当作契约;其余的在两边各自配置。
7.10 栅格样式
7.10.1 图像调整
栅格图层的 Style 面板首先给出四个纯视觉参数:
| 参数 | 范围/单位 | 作用 |
|---|---|---|
| Brightness | 最小/最大 | 提亮或压暗 |
| Saturation | —— | 色彩饱和 |
| Contrast | —— | 对比 |
| Hue Rotate | 度 | 色相旋转 |
这些只是显示层调整,不改像素值。GeoTIFF、COG 与瓦片栅格都适用。在 ArcGIS 引擎下它们落成 SDK 的 CSS 滤镜 effect。
7.10.2 Raster symbology
GeoTIFF 与 COG 额外有 Raster symbology 一节,核心是 Render mode:
| 模式 | 参数 | 用途 |
|---|---|---|
| Single band (pseudocolor) | Band、色带(可反转或自定义颜色)、可选 Classify into discrete classes(equal interval / quantile / manual) | 高程、温度、指数等单波段数据 |
| RGB composite | 三个波段映射到 R、G、B | 真彩色/假彩色合成 |
| Index (normalized difference) | 两个波段计算归一化差值指数(如 NDVI) | 植被、水体、雪等指数 |
通用参数:
| 参数 | 说明 |
|---|---|
| Min / Max | 拉伸范围;留空显示 auto |
| Stretch | Linear、Logarithmic、Square root |
| Gamma | 中间调调整 |
| No data | 从文件读 nodata、渲染每个像素、或指定自定义值 |
提示:Classify into discrete classes 打开后,色带从连续渐变变成离散分级,图例会跟着变成分类行而不是色条。这就是”高程带”与”高程渐变”的区别。
7.10.3 直方图与拉伸
Histogram 一节显示像素值分布,并在两个手柄之间给当前拉伸窗口着色:
- 拖动手柄设置最小/最大,Min/Max 输入框随之更新;反向也成立;
- 键盘:聚焦手柄后用方向键微调,
Shift或Page Up/Page Down走十倍的步长,Home/End到两端; - RGB composite 每个通道一张直方图,各有自己的 Min/Max;编辑一个通道时其余通道被固定在当前自动范围,不会跳动;
- 没有设置范围时,自动窗口是波段的第 2 到第 98 百分位。
第 2–98 百分位而不是 0–100,是为了避开两端的离群值把整幅图拉平——这是遥感影像拉伸的常规做法。
7.10.4 视口拉伸
对未分级的单波段或指数栅格,Viewport stretch 把范围拟合到当前屏幕上的像素:
| 选项 | 含义 |
|---|---|
| Viewport min / max | 用视口内的极值 |
| Viewport 5–95 percentile | 用视口内的 5%–95% 分位 |
| Viewport mean ± 2 standard deviations | 均值 ± 2 倍标准差 |
选完点 Apply to viewport 生效。勾选 Update automatically when the map moves 后,平移缩放会持续重拟合。拉伸随图层保存。
7.10.5 光谱剖面
对多波段栅格(堆叠的 Landsat/Sentinel 场景、NetCDF/HDF 立方、任何多于一个波段的 COG),Style 面板会增加 Spectral profile 图:单个像素在各波段上的值。
使用流程:
- 在 Layers 面板为该图层打开 Identify;
- 点击地图;
- 每次点击在地图上加一个编号点,并在图里加一条对应曲线——可以点水体、植被、沥青,比较三种响应;
- 最多同时比较 6 个点,更早的点会随新增老化掉。
细节:
- 有波长声明时按波长作图,否则按波段号;波长列表与波段数不匹配时会被忽略而不是被信任,避免用一份过期元数据标错横轴;
- 只取包含点击像素的那一个瓦片,不取整景,因此对大型远程 COG 也很快;
- Pop out 把图浮成可拖动、可缩放的窗口;PNG 与 CSV 可导出;
- 单波段栅格不产生剖面——用 Identify 直接读像素值;
- 点在栅格之外、或该像素在所有波段都是 nodata 时,点击被丢弃,不影响已收集的点。
7.10.6 WMS 图例
WMS 图层额外有 Legend 一节:
- Get legend 从能力文档的
LegendURL取图例,没有就发GetLegendGraphic请求; - 服务没有图例时,可以自行填一个图例图片 URL;
- Add to map 把图例放进地图上的 HTML 控件。
7.11 图例、色带与 Style Manager
7.11.1 自动图例
Controls → Legend 打开地图图例。它由 apps/geolibre-desktop/src/lib/auto-legend.ts 的 buildAutoLegend 从当前可见图层的符号化派生,不需要你手写:
| 图层情况 | 图例产出 |
|---|---|
| 单值矢量 | 名称 + 几何形状色块 |
| 分级/分类矢量 | 每个分类一行,字段名作为标题 |
| 规则渲染 | 每条启用的规则一行 |
| 比例符号 | 尺寸阶梯行(半径或线宽) |
| 热图 | 连续渐变条 |
| 连续色带栅格 | 渐变条 + 两端数值 |
| 分类栅格 | 分类行(含 Raster Attribute Table 的地类标签) |
| 图表符号 | 各切片/条形的色块行 |
| 几何生成器 | 派生的质心/多边形行 |
几条边界:
- 每条目最多 100 行(
MAX_LEGEND_ROWS),超出的尾部会被省略; - 几何感知:标题行旁的色块按图层几何画成多边形、线或点,点还会画成标记形状;
- 分级图层在字段名下标注分类方法与级数,例如 “Quantile, 5 classes”;手改过断点的标为 manual;
- 透明度是复合的:图例色块带着图层(与分组)的复合不透明度,视觉上与地图一致;
- 手写条目(
customEntries)可以替换某图层的派生行,或作为独立分节出现;逐项覆盖(重命名/隐藏/重排)叠加在派生结果之上; - 计算不依赖 React、地图与 i18n,因此可单元测试,语言与色图解析由面板注入。
7.11.2 Colorbar
Controls → Colorbar 是连续色标,用于栅格值:它把当前拉伸窗口与色带画成一条带刻度的条。与 Legend 的分工:
| 控件 | 适合 | 内容 |
|---|---|---|
| Legend | 多图层、分类/分级/规则/图表 | 每个图层一节,逐类色块 |
| Colorbar | 单幅连续栅格 | 一条渐变 + 数值刻度 |
两者都随工程保存,也都能被 Record Video… 烧进录像。
7.11.3 Style Manager
Settings → Style Manager 是个人符号化预设库,入口还有 Layer actions → Styles → Saved styles (Style Manager)…。
| 能力 | 说明 |
|---|---|
| 保存 | 把当前图层的符号化存成预设 |
| 预设种类 | 样式(style)、符号(symbol)、标注(labels)、色带(ramp) |
| 应用 | 应用到任何工程里的任何图层 |
| 管理 | 重命名、打标签 |
| 工程级条目 | 工程文件里的 styleLibrary 数组(name、tags、kind、LayerStyle 子集),为空时省略 |
提示:
styleLibrary随工程走,因此一个团队可以共用一套符号标准:把标准样式存进模板工程,成员新建工程时它就在库里。
7.12 色觉预览
View → Color vision preview 把地图模拟成不同色觉类型看到的样子:
| 模式 | 类型 |
|---|---|
| Protanopia | 红色盲 |
| Deuteranopia | 绿色盲 |
| Tritanopia | 蓝色盲 |
| Achromatopsia | 全色盲 |
关键行为:
- 只过滤地图——所有渲染引擎与分屏面板,以及地图上的图例;面板与菜单不受影响;
- 开启时地图上有徽标,点徽标的 × 或选 Off 结束;
- 仅本次会话有效,不写进工程;
- 不影响导出:Print Layout、地图图片导出、Record Video 抓的都是地图的真实颜色。只有窗口截图才会带上模拟效果——这正是徽标存在的理由。
它最典型的用法是检查”红绿分级”是否还读得出来:把一张用 RdYlGn 的分级图切到 deuteranopia,如果两级变得几乎一样,就换 viridis 或 cividis。
7.13 常见样式问题
| 现象 | 最可能的原因 | 处理 |
|---|---|---|
| 改了样式地图没变化 | 没点 Apply style type(分级/分类) | 点应用;或改用单值/表达式 |
| 分类着色对图标无效 | 用了位图标记,颜色只对内置形状与 SVG 生效 | 换 SVG 标记 |
| 分级只出了 3 级而不是 5 级 | 分位断点重复被合并 | 换 Natural breaks 或手动加级 |
| 标准差分级级数变少 | 越界断点被丢弃 | 正常行为;换方法或减级 |
| 图例说明写的是旧方法 | 断点被手改过但方法名未更新 | 重新应用一次分级,或选 Manual |
| 标注只出现一部分 | 碰撞避让 | 开 allowOverlap 或设 priorityExpression |
| 数字标注没有千分位 | numberFormatEnabled 未开,或标签用了表达式 |
开开关;表达式里用 number-format |
| 过滤后聚合气泡计数不对 | 时间轴/规则过滤在聚合后生效 | 切回 Single symbol(见 7.3) |
| 导出的 SLD 丢了混合模式 | 目标格式没有对应项 | 属预期;在目标端重配 |
| 弹窗里出现一堆陌生列 | 没配 Fields | 用 Popup 设计器挑字段并加标签 |
| 弹窗设计器不出现 | 图层仍在 Add Vector 面板手里 | 见第 5 章 5.2.8 |
| 栅格一片死黑 | Min/Max 未设,被离群值拉平 | 用直方图设 2–98 百分位 |
| 光谱剖面画不出 | 单波段栅格 | 用 Identify 读像素值 |
7.14 本章小结
- 一份样式记录,四种引擎编译。
LayerStyle是引擎无关的声明式记录,因此样式能保存、导出、跨引擎;也因此在没有 Style Specification 的引擎上部分能力缺席。 - 基础符号包括填充、描边(像素或米)、点/标记、图案填充、线装饰、几何生成器、反向填充、拉伸与 Z 值渲染。
- 点渲染器三选一:单值、热图(半径/强度/色带/权重)、聚合(半径/最大缩放);聚合与逐要素渲染过滤存在已知的计数偏差。
- 五种样式类型:单值、分级、分类、规则、表达式;分级有六种方法,断点是每级下界且必须严格递增,重复断点会被合并。
- 20 条内置色带与栅格色图一一对应;采样是锚点间线性插值。
- 表达式构建器提供精选函数表、
@变量(一次性替换)、基于样式规范的校验、以及容忍尾逗号的解析。 - 标注支持字段/表达式、锚点偏移旋转、比例尺范围、数字格式、去重与五个数据驱动覆盖表达式;不同引擎在旋转、碰撞、优先级上有差异。
- 弹窗设计器决定分享出去的地图显示什么,隐藏字段不会被重新暴露。
- 快速过滤是持久化的控件状态,编译发生在同步时;清空最后一个勾选等于显示全部。
- 互通四种格式:GeoLibre URL style、Mapbox GL style、OGC SLD、QGIS QML;按内容判断格式;混合模式与自定义标记会有损。
- 栅格样式分图像调整与符号化两层,直方图默认 2–98 百分位,多波段可做光谱剖面。
- 图例自动派生,每条目最多 100 行;色觉预览只影响地图与地图上的图例,不影响导出。
7.15 实践练习
-
分级方法对比:用一份人口数据做四次分级(Equal interval、Quantile、Natural breaks、Geometric interval),各 5 级,记录断点数值,并说明哪一种最能反映”少数大城市 + 大量小城市”的分布。
-
断点去重:构造一份含大量重复值的字段(例如把某个类别编码复制成数值列),用 Quantile 分 6 级,记录实际得到的级数,并解释断点为什么会被合并、MapLibre 为什么要求严格递增。
-
标注设计:给一个点图层配置标注,要求——按人口决定字号、低于 10000 不标、碰撞时优先显示人口大的、数字带千分位。写出你需要设置的五个字段与对应的表达式思路。
-
表达式排错:故意写三条错误表达式(一条尾逗号、一条算符拼错、一条参数个数不对),分别在构建器里验证,记录错误码与提示,并说明尾逗号为什么能通过。
-
样式互通:把一份配置了分类着色、图案填充与标注的图层依次导出为 GeoLibre URL style、Mapbox GL style、SLD、QML,再分别导入一个新图层,逐项记录哪些设置被保留、哪些丢失,并解释原因。
-
栅格拉伸:加载一幅高程 COG,先把 Min/Max 留空观察效果,再打开直方图把窗口设到 2–98 百分位,然后切到 Viewport 5–95 percentile 并勾选自动更新,比较三种设置下地图的观感差异。
延伸阅读
- 仓库文档:
docs/user-guide/styling.md、docs/user-guide/layers.md(样式互通菜单)、docs/user-guide/interface.md(色觉预览) - 源码:
packages/core/src/color-ramp.ts、packages/core/src/vector-color.ts、packages/core/src/expressions.ts、packages/core/src/types.ts、packages/map/src/sld-import.ts、packages/map/src/qml-import.ts、apps/geolibre-desktop/src/lib/auto-legend.ts - 项目主页:https://geolibre.app