尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

GeoServer仿百度地图样式实战:CSS Style配置与OpenLayers对接

GeoServer仿百度地图样式实战:CSS Style配置与OpenLayers对接 简介仿百度GeoServer Style是一套面向GeoServer的样式配置包专门解决GIS地图渲染风格单一的问题适合需要为河流、道路、地铁等要素模拟百度地图色彩与线型规则的地图开发者和GIS爱好者。包内共45个文件以23个SLD样式文件为核心配合20个XML配置文件及SVG、PNG图标和填充资源整体仅22KB体积轻巧便于直接导入并按Zoom层级调优实现要素分级显示。已有803人学习/下载可结合附带的“北京地图2014”数据构建具有百度风格的历史地理信息地图用于城市规划、交通分析、公众服务等场景。通过阅读其中的SLD规则还能掌握颜色、线宽、填充、标签及道路等级、河流宽度与地铁线路等属性设置思路为自定义地图外观提供可复用的方法与模板。 搞GIS的人应该都有过这种念头打开百度地图看着人家的配色、注记、道路分级和交互方式然后低头看看自己WebGIS系统里那个默认的OpenLayersGeoServer出图怎么看怎么别扭。于是“仿百度GeoServer Style”这个需求就来了——不是让你真的去逆向百度地图而是用GeoServer这套开源服务把底图的视觉风格和交互体验尽量往百度地图的方向靠。这篇文章写的就是这件事用什么数据、怎么配Style、前端怎么接、中间会踩哪些坑尤其是那个让人抓狂的error while setting feature type、flushing cache and retry。无论你是刚接触GeoServer的新手还是被样式折磨过几轮的老人这篇都适合你花十分钟过一遍。1. 项目定位所谓“仿百度GeoServer Style”到底在做什么1.1 先拆清楚需求别做成四不像很多人在网上搜“仿百度GeoServer Style”其实脑子里想的是两件完全不同的事一种是想要“百度地图那种浅灰底、白色道路、蓝色水系”的底图视觉效果另一种是想要“类似百度地图的交互体验”比如拖拽平滑、滚轮缩放、点击气泡、底图切换这些。这两件事难度的差距非常大前者只需要在GeoServer里慢慢调SLD或CSS样式就够了后者还牵扯到前端地图库的交互定制工程量大得多。我建议接需求之前先问清楚是截图级的视觉相似还是操作级的体验相似。多数情况下业务方要的只是视觉上“看上去像百度”因为交互层面OpenLayers和百度地图JS API本来就很接近不需要额外仿。等需求确认成“视觉风格统一、信息层级清晰”之后事情就变成了一个纯粹的GeoServer样式工程范围一下小了很多。还有一种情况要警惕有人拿着网上“仿百度样式”的截图要求做成“一模一样的瓦片”。这里有个认知错误——百度地图的底图数据是人家采集的道路、水系、绿地的几何精度以及道路分级的字段属性你手上不一定有。没有数据基础样式表再努力也画不出“一模一样”。所以前期一定要声明确所谓仿Style是在自己的数据基础上修改符号、配色、注记规则让观感接近而不是套用别人的瓦片。1.2 技术栈选择为什么是GeoServerPostGISOpenLayers这套组合是开源GIS里最成熟的链路。GeoServer负责把空间数据发布成WMS/WMTS服务PostGIS存矢量数据OpenLayers在浏览器端负责请求瓦片和交互。相比MapServer或者自研渲染引擎GeoServer最舒服的地方在于样式更新不需要重启服务靠SLD/CSS样式文件动态加载配合REST API还能远程管理样式适合“调样式调到吐”的迭代过程。如果你所在环境里用的是Leaflet也可以但我会更推荐OpenLayers做“仿百度”这件事它自带缩放控件、旋转控件、比例尺控件的默认样式很接近互联网地图的风格改起来也直接不用像Leaflet那样另找插件。当然如果你只是需要简单展示Leaflet也够但涉及多图层叠加的层级控制、EPSG:3857投影转换和WMS参数调试OpenLayers的调试面板和社区案例明显更丰富。版本方面GeoServer我建议直接用2.23.x或更新的稳定分支截至写作时2.25也出来了。旧版本在SLD 1.0的符号化支持上有不少边角问题尤其文字注记的避让规则新版本处理得明显更好。2. 百度地图样式拆解与Style方案设计2.1 它好看在哪儿先把视觉元素一项项拆开既然目标是“仿”就不能凭感觉瞎调色。我习惯先截一张百度地图的底图放在绘图软件里吸色拆层级。百度底图有几个很明显的特征底色是浅灰偏白不是纯白整个底图非常安静因为真正的主角是道路和标注。道路是分层的高速/快速路是黄色带橙色描边或填充主干道是白色带浅灰描边次干道微弱白支路直接不显示或极浅灰。线宽从高到低递减不同层级的视觉优先级很清楚。水系是淡蓝色饱和度低边界圆润。绿地和公园用浅绿色填充边界基本不描边。注记文字标签全是深灰色道路名字号小但清晰兴趣点名称的字号大于道路名并使用黑色加粗风格。关键地标和分级图标有但在底图切片上不会抢视觉。把这些特征转译成GeoServer的样式语言就是给不同的要素类道路等级、水系类型、绿地类别分别写符号规则Rule然后控制好渲染顺序和比例尺范围。2.2 GeoServer里做Style的三条路SLD、CSS、YSLDGeoServer的样式不止一种写法。最常见的是SLDStyled Layer Descriptor基于XML网上资料最多各种教程都是拿它举例。缺点也明显写起来极其啰嗦一个简单的点符号都要十几行。CSS样式是GeoServer后来推的方案语法更接近前端CSS写起来短得多而且支持变量和嵌套规则做复杂的道路分级非常省力。YSLD是CSS的YAML版现在用的人不多了解即可。我的建议是新项目直接上CSS Styling老项目才去改SLD。如果你在Web界面里新建样式默认会让你选语言选CSS就可以。CSS最终会被GeoServer编译成SLD执行所以底层一致不用担心兼容性。网上搜“仿百度样式”时搜到SLD代码居多如果你用CSS看懂之后再翻译过去即可并不冲突。2.3 一套能落地的样式设计模板我在实践里常用一种“分层控制变量统一”的设计模板先定义一组全局变量底色、路宽、注记字号再逐个要素类写规则。CSS Styling里可以用开头定义变量例如roadPrimaryColor: #ffffff; roadPrimaryOutline: #d0d0d0; waterColor: #aad3df; greenColor: #c8dfa0;这样后面调色只改变量不用满文件找颜色值效率高很多。对道路按kind或class字段区分等级把最高等级的规则写在最前面对水系优先填面再渲边界对绿地直接fill一个低饱和绿色不要描边。标注的font-family最好指定中文字体例如Microsoft YaHei, PingFang SC, sans-serif否则某些环境里中文可能显示成一串方框。具体写CSS Style之前你会需要一个稳定的数据底子至少有一个包含道路、水系、绿地或土地利用的面/线图层并且属性字段里有能区分等级的字段。没有的话一个union了Osm数据的PostGIS库是常见选择网上有osm2pgsql之类的工具可以导相关教程很多这里不展开。3. 实操从零搭建一套仿百度风格地图服务3.1 准备数据与GeoServer工作区假设你现在已经有一份PostGIS数据里面有public.roads、public.water和public.green三张表。第一步是在GeoServer里新建工作区Workspace名字建议用全小写不带特殊字符例如baidu。然后新建数据存储Store类型选PostGIS填好数据库连接参数。这里有个小坑连接池参数里的Connection timeout建议设成20秒不然数据量大时首次请求容易超时。图层发布时需要注意设置边界Bounding Box尤其是从PostGIS导入时经常自动计算的边界不对。在“Tile Bounding Boxes”区域点“Compute from native bounds”然后确认在EPSG:3857下计算一次因为浏览器端地图通常用Web Mercator投影如果缺了3857的边界前端会出现底图偏移或者白屏。发布完图层之后打开Layer Preview如果能看到默认样式下的数据说明服务链路通了。此时GeoServer自带的默认样式很难看不要急后面就是Style上场的时候。3.2 用CSS Styling写一套百度风底图在GeoServer左侧菜单进入“Styles”点击“Add a new style”。语言选CSS填一个像baidu_base的名字。下面是我自己摸索出来的一套精简可用的CSS模板以道路和水系为例/* 仿百度地图-底图样式 */ waterColor: #aad3df; waterStroke: #89b9c9; roadPrimary: #ffd78d; roadPrimaryStroke: #e8b45a; roadSecondary: #ffffff; roadSecondaryStroke: #cdcdcd; roadStreet: #eaeaea; greenSpace: #c5dfa8; * { stroke-width: 1; font-family: Microsoft YaHei, PingFang SC, sans-serif; } /* 绿地渲染优先级最低 */ [featureType green] { fill: greenSpace; stroke: none; } /* 水系 */ [featureType water] { fill: waterColor; stroke: waterStroke; stroke-width: 0.5; } /* 道路主层级 */ [featureType road][class primary] { stroke: roadPrimary; stroke-width: 4; z-index: 2; } [featureType road][class primary] { stroke: roadPrimaryStroke; stroke-width: 5; z-index: 1; } /* 次干道 */ [featureType road][class secondary] { stroke: roadSecondary; stroke-width: 2.5; stroke-linecap: round; z-index: 3; } /* 支路 */ [featureType road][class street] { stroke: roadStreet; stroke-width: 1; z-index: 2; } /* 道路名称标注 */ [featureType road][class primary] { label: [name]; font-size: 12; font-fill: #666666; halo-color: #ffffff; halo-radius: 1.5; label-auto-wrap: 60; label-priority: 100; }这段代码的思路是用featureType和class做条件过滤绿色面垫底水系次之道路最上层。z-index控制同一图层内不同特征的相对层级能解决同级道路互相压盖的问题。多叠加一个更深色的描边是为了模仿百度道路的“描边增加辨识度”的处理。注释里写清楚了规则对应的视觉目标后来维护的人容易看懂。写好之后保存回到Layer Preview或者直接在前端调用如果这一步就出现白屏或渲染错位多半是数据字段名和样式条件不匹配先回图层属性页确认字段名。3.3 前端OpenLayers对接与调试前端用OpenLayers加载GeoServer的WMS或WMTS。最简单的方式是用TileWMS源import TileLayer from ol/layer/Tile.js; import TileWMS from ol/source/TileWMS.js; const baiduStyleLayer new TileLayer({ source: new TileWMS({ url: http://localhost:8080/geoserver/baidu/wms, params: { LAYERS: baidu:roads, STYLES: baidu_base, TILED: true, VERSION: 1.1.1 }, serverType: geoserver }) });有两点值得注意。第一STYLES参数必须和样式名一致如果不传GeoServer会默认用图层发布时选择的一个样式。我遇到过前端显示出来的样式和自己在Styles页调好的完全不一样查半天发现是发布了多个样式默认样式指向了另一个。第二TILED: true让GeoServer按瓦片方式渲染响应速度快很多但如果你引用的是一个跨域域名需要确认GeoServer开启了CORS否则浏览器报跨域错误地图一片白。地图容器和View的投影记得设置成EPSG:3857中心点选在你的数据范围内。比如你在北京附近new View({ center: [12960000, 4850000], zoom: 11, projection: EPSG:3857 })如果你把缩放的层级放到14级以上会发现道路的宽度和标注不再变化。这是因为CSS样式默认在所有缩放等级都生效而仿百度底图的等级体系需要“小比例尺只看大路、大比例尺才显示小路”这种scale-based规则。GeoServer里用scale或scale规则来控制显示范围比如[featureType road][class street] { stroke: roadStreet; stroke-width: 1; scale: [scale 10000]; }意思是10万比例尺以下不显示街道只在放大的时候冒出来。不同家底图的分级阈值不是标准答案建议对着截图多试几次。4. 高频报错与排查实录4.1 “error while setting feature type, flushing cache and retry”到底什么情况这个报错我最初碰到时也是一头雾水。它通常出现在你发布新图层、修改样式或者上传SLD之后GeoServer界面弹出一句error while setting feature type, flushing cache and retry有时候还伴随一个title.../title一样拼接出来的异常页面那种夹着HTML的乱码报错确实很吓人但真正有用的其实是里面包着的异常信息。这个问题的本质是GeoServer在收到修改图层的请求后需要更新内部的FeatureType缓存同时刷新GeoWebCacheGWC的缓存。如果这个过程被中断——比如图层正在被大量请求占用、磁盘空间满了、或者目录权限不对——就会触发这个错误。它叫“flushing cache and retry”说明GeoServer自己已经尝试刷新缓存并重试但失败了。排查步骤我建议按这个顺序先看GeoServer的日志文件一般在安装目录的logs/geoserver.log或data_dir/logs/下。搜索flushing cache或ERROR关键字往往能看到具体的异常栈大多数是java.io.IOException或NullPointerException。排查数据目录权限。GeoServer的data目录如果所在磁盘满了缓存刷新必然失败df -h看一眼。停掉GeoServer清掉GWC缓存目录gwc目录下对应图层名的文件夹再重启。很多情况下这招立竿见影。如果还是不行尝试用REST API重建图层。清理GWC缓存这个操作在开发环境里没有任何风险顶多重渲染一次。生产环境如果缓存膨胀到几十GB建议精准删对应图层缓存不要整个删除。4.2 OpenLayers打开GeoServer报500“geoserver的打开openlayers报500”这类问题网上频率极高。报500的时候浏览器F12面板里能看到请求指向了/geoserver/baidu/wms?serviceWMS...响应体是GeoServer的异常信息。最常见的三个原因第一图层名或工作区名拼错。注意LAYERS参数是工作区名:图层名中间多了或少了一个冒号都会报500。第二样式名称不对或者样式文件内部有语法错误。项目里调样式时经常一边改样式一边刷新前端改到一半保存了一个语法不完整的CSS结果前端立刻白屏。第三请求参数里同时传了SRS和CRS或者投影名写成了EPSG:4326但View是EPSG:3857容易触发投影转换异常。遇到500别慌直接拿浏览器请求地址拼到地址栏里去掉TILEDtrue等参数改成单张图片请求Geoserver会返回更加直白的XML错误提示有些甚至直接告诉你哪个Style name无效比在前端调试台瞎猜快得多。4.3 GeoServer插件与扩展资源去哪找配套需求里需要装插件的情况也很多比如你要发布矢量瓦片Vector Tile得装GeoServer Vector Tiles扩展要输出PDF或Excel要装Print扩展。很多新手找插件地址时被各种第三方下载站绕晕甚至下到捆绑脚本。正路永远只有一个打开GeoServer官网的下载页面选择和你当前版本号严格对应的“Extensions”目录里面按功能列好了zip包。下载后解压到WEB-INF/lib重启即可。这里额外提醒一点GeoServer的插件版本必须和主程序完全一致。比如主程序是2.23.2插件也必须是2.23.2差一个小版本都可能在启动时报类冲突。loaded库冲突是新手最容易踩的不要小看。5. 一些个人折腾下来的心得仿百度Style这件事表面看是技术活认真做完之后我才发现它更考验“设计拆解”的耐心。样式文件里每个颜色、每条stroke-width背后都对应着“你希望用户第一眼看到什么”的决策。不要试图一步到位先把底色和水系调好再逐级加道路最后补标注每次只改一个变量然后在浏览器里反复缩放检查。我自己的习惯是专门建一个“样式预览”页面把几个关键缩放等级固定成书签每次保存样式后切到不同等级截图对比避免在一个层级调完发现另一个层级图标挤成一团。再到后面你还可以给不同道路等级的注记单独设置label-priority和label-auto-wrap让文字避让更自然这些小细节才是“仿得真不真”的关键。最后再分享一个小技巧CSS Style文件建议用Git管理起来因为样式微调太容易反复有天你突然觉得原来的配色更好看git diff一下就回得去比在GeoServer界面里复制来复制去靠谱得多。GeoServer作为开源项目花样很多但核心不外乎“数据样式缓存”三件事把这三点理顺了后面做什么风格都顺手。本文还有配套的精品资源点击获取
返回列表