做WEBGIS开发这几年,最常被新人问到的一个问题就是:"空间数据到底是怎么从数据库一路渲染到浏览器里的?"很多人玩过Leaflet画点线面,也听过PostGIS和GeoServer,但真正把"创建空间数据库、发布数据、调用WMS服务"这条完整链路串起来跑通的人,其实不多。这篇文章就用一个最小可复现的全栈演示,把这整条链路彻底讲透——从PostgreSQL里建一张空间表,到GeoServer把它发布成标准的WMS服务,再到前端用OpenLayers把这个服务加载渲染出来。你跟着操作一遍,就能理解WEBGIS里"数据层—服务层—展示层"三者之间是怎么协作的。整个演示不依赖任何商业GIS平台,全用开源组件,适合正在做GIS课程设计、毕业设计,或者刚开始接触WEBGIS的开发者。
先说清楚这个全栈项目具体做了什么。我把一套完整流程拆成四个环节:准备一套带坐标的矢量数据,把它装进PostgreSQL并启用PostGIS空间扩展;用GeoServer连接这个空间数据库,把表发布成Web地图服务;写一个最简单的HTML页面,用OpenLayers调用这个WMS服务并叠加显示;最后通过点击地图实现要素属性查询。这四个环节跑通之后,你对WEBGIS系统的"数据怎么存、服务怎么出、前端怎么调"就有了一个整体的认知框架。下面我按实际操作的顺序,把每一步的选择理由和踩坑点都讲清楚。
1. 整体设计思路与选型拆解
1.1 为什么选这条技术路线
市面上能做WebGIS的技术组合其实不少,有商业GIS平台,也有纯前端的GeoJSON方案,还有直接用PostGIS查数据生成接口的方案。我推荐这套"PostgreSQL + PostGIS + GeoServer + OpenLayers"组合,原因很直白:这四个组件全部开源免费,而且各自负责的环节边界非常清晰,特别适合理解原理。
数据库选PostgreSQL是因为PostGIS是目前功能最完善的开源空间数据扩展,点线面、缓冲区、空间索引、拓扑分析都能做,数据量大了也不会崩。服务端选GeoServer是因为它把"数据库里的表变成网络地图服务"这件事封装得足够简单,而且原生支持WMS、WFS、WCS这些OGC标准,发布完之后无论是OpenLayers、Leaflet还是ArcGIS客户端都能直接调。前端选OpenLayers则是看中它对WMS/WMTS这些老牌协议的支持最完整,文档例子多,遇到问题也好搜。
对比一下其他方案就能理解这个选择:如果全部用GeoJSON,把几千个要素直接塞给前端,数据量一大浏览器就卡死,而且每次更新数据都要重新生成文件;如果用纯后端返回数据、前端自己画图,等于把地图渲染逻辑全部重写一遍,工程量直接翻倍。而走WMS这条路,切片和渲染都由GeoServer完成,前端只需要发请求和贴图片,性能和可维护性都更好。
1.2 数据流转与每一层的职责
整套系统的数据流可以这样理解:PostGIS数据库负责存数据和算数据,GeoServer负责把数据变成图片,浏览器负责展示图片和响应用户操作。三者各管一段,互不越界。
举个例子,用户在前端看到一张"道路分布图",真实过程是:OpenLayers根据当前视图范围拼出一个WMS请求,发到GeoServer;GeoServer收到请求之后,从PostGIS里查出落在该范围内的道路要素,按样式配置渲染成一张PNG图片,再返回给浏览器;浏览器把这张图片贴在对应位置上。用户拖拽地图时,前端会不断计算新的视图范围并重新发请求,于是就有了一种"地图跟着手走"的流畅感。
这里有个关键认识:WMS服务返回的不是矢量数据,而是一张已经渲染好的图片。你无法在前端直接拖拽某个点、修改某条线的属性,那需要走WFS服务,是另一套逻辑。正因为WMS是"图片接口",所以它跨平台、跨客户端兼容性极好,这也是我特别推荐先把它跑通的原因——只要这个链路通顺了,后面再学WFS、WMTS都只是加参数的事。
2. 环境准备与工具版本组合
2.1 需要用到的软件清单
动手之前先把工具备齐。我这次演示用的是目前比较稳定的版本组合,你可以直接照抄:
| 组件 | 推荐版本 | 作用说明 |
|---|---|---|
| PostgreSQL | 15.x | 基础数据库,存储矢量数据 |
| PostGIS | 3.4.x | 空间扩展,需与PostgreSQL版本匹配 |
| GeoServer | 2.24.x | 地图服务发布中间件,需Java环境 |
| OpenLayers | 8.x/9.x | 前端地图库,通过CDN引入 |
| QGIS | 3.x | 辅助工具,用于检查数据、测试连接 |
Java环境建议装JDK 11或JDK 17,GeoServer 2.24默认支持这两个版本,不要装太新的JDK 21,个别依赖解析会出幺蛾子。操作系统Windows、Linux、macOS都可以,路径里尽量不要出现中文,GeoServer对中文路径的支持一直不太友好。
如果你手头没有矢量数据,可以在网上找一些公开的测试数据,比如世界河流面、城市道路线、行政区划面等shp格式文件。也可以用QGIS自己画几个点线面要素再导出,数据量不用大,能把流程跑通就行。
2.2 安装过程中的关键注意事项
PostgreSQL安装时要注意,安装器会让你设置postgres超级用户的密码,这个密码后面用得到,别设完就忘。装好之后记得把数据库的bin目录加到系统环境变量里,否则命令行敲psql会提示找不到命令。Windows下默认是C:\Program Files\PostgreSQL\15\bin。
PostGIS扩展的安装建议用Stack Builder(PostgreSQL安装器自带的组件),勾选PostGIS相关选项,它会自动把geos、proj这些依赖一起装掉。如果选择手动安装,经常因为缺少底层依赖库导致CREATE EXTENSION postgis时报错,这个坑我踩过不止一次。
GeoServer的安装有两种方式:Windows安装包或者解压版(zip包)。我推荐用zip解压版,因为它不写入注册表,换版本、改配置都方便。解压之后进入bin目录,Windows下双击startup.bat启动,Linux下运行./startup.sh,看到Geoserver 2.24.0 started successfully的提示就是启动成功了。默认端口8080,启动完成后浏览器访问http://localhost:8080/geoserver,初始账号是admin,密码是geoserver,进入后建议第一时间改掉。
3. 创建空间数据库并装载矢量数据
3.1 初始化空间数据库与PostGIS扩展
打开命令行,用psql连接到默认的postgres数据库,先创建一个专门用于演示的数据库,我给它取名gisdb:
psql -U postgres -h localhost如果你用的是Windows,可能会弹出密码输入提示。连接成功后,执行以下SQL:
CREATE DATABASE gisdb; \c gisdb CREATE EXTENSION postgis;创建数据库后,关键一步就是CREATE EXTENSION postgis。这个命令会在当前数据库里注册几百个空间函数、数据类型和元数据表,执行成功后你可以通过以下SQL验证一下:
SELECT postgis_version();如果返回类似3.4 USE_GEOS=1 USE_PROJ=1的结果,说明空间扩展已经启用成功。这里有一个小细节:PostGIS的扩展是按数据库启用的,你创建100个数据库,就需要在100个数据库里分别执行CREATE EXTENSION postgis,它不会自动全局生效。
另外补充一个使用习惯方面的建议:所有SQL脚本文件,记得用UTF-8编码保存再执行。如果你用SQL Server或者Oracle习惯了默认编码,到PostgreSQL这儿很容易踩到中文乱码的坑,因为PostgreSQL默认客户端编码是UTF8,脚本文件如果是GBK,中文会直接变成问号。
3.2 矢量数据导入的三种方式对比
数据入库是让很多初学者头疼的环节,其实方式很多,我按推荐程度排一下。
第一种方式,也是最推荐的方式——使用PostGIS自带的shp2pgsql命令行工具。假设你有一个名为roads.shp的线状道路数据,想把它导入gisdb数据库的public模式下,命名为roads表,命令如下:
shp2pgsql -s 4326 -W UTF-8 -I roads.shp public.roads | psql -U postgres -h localhost -d gisdb这里的参数要解释一下:-s 4326表示源数据的空间参考系编号,EPSG:4326是WGS84经纬度坐标系,国内大多数公开数据都是这个坐标系;-W UTF-8指定shp文件属性表里的字符编码;-I会在导入完成后自动创建空间索引,这个索引对大数据量的空间查询至关重要。如果你不确定shp文件是什么坐标系,可以用QGIS打开,在图层属性里查看,或者干脆用ogrinfo命令行查询。
第二种方式是用QGIS的"DB Manager"插件。打开QGIS,加载shp数据,在菜单栏找到Database -> DB Manager,连接到gisdb之后,用"Import Layer/File"功能把图层拖进数据库。QGIS的优势是图形化界面直观,还能实时看到导入结果的坐标范围和要素数量,适合不习惯命令行的用户。
第三种方式是先建表再用SQL写入,适合批量处理和自动化场景。比如你已经建好了表结构,可以用COPY命令导入属性数据,再用AddGeometryColumn函数添加空间字段。这种方式灵活但步骤较繁琐,日常演示其实用第一种就够了。
3.3 数据入库后的验证检查
数据导入不等于万事大吉,我在实操中基本都会做一轮检查,确认数据没问题再继续往下走。
先确认表结构:
\d roads如果表里正确存储了空间字段,会看到geom geometry(LineString,4326)这一类的字段定义。然后看一下要素数量和空间范围:
SELECT COUNT(*) FROM roads; SELECT ST_Extent(geom) FROM roads;ST_Extent会返回这个表里所有几何对象的坐标范围,比如BOX(116.3 39.8,116.5 40.0),这说明数据是正常的经纬度范围。如果返回空值,说明geom字段里没有任何数据,极大可能是导入时没选对几何字段。
再用空间函数抽查一下数据的有效性:
SELECT name, ST_AsText(geom) FROM roads LIMIT 5;ST_AsText能把二进制几何对象直接转成WKT文本,比如LINESTRING(116.3 39.8,116.4 39.9),方便肉眼确认坐标值是否合理。做完这些基础检查,空间数据库这边就算准备好了。
4. GeoServer连接PostGIS并发布WMS服务
4.1 工作区与存储配置
打开GeoServer管理界面,默认地址http://localhost:8080/geoserver,使用admin账号登录。发布一个数据源一般分三步:创建工作区、添加存储、发布图层。
第一步,创建工作区。在左侧菜单点"工作区",然后点击"添加新的工作区"。工作区名称我用的是gisdemo,命名空间URI我填的是http://localhost:8080/gisdemo。这两个字段是WMS服务URL的一部分,工作区名称会出现在图层访问路径里,比如后面图层完整名称就是gisdemo:roads。命名空间URI通常填一个不存在的URL或域名,它只是个标识符,但很多人习惯填成一个真实地址,这不影响功能。
第二步,添加存储。在左侧菜单点"存储",再点"添加新的存储",在矢量数据源分类下选择"PostGIS"。进入配置页后,基础信息里填写前面创建的空间数据库连接参数:
- 主机:localhost
- 端口:5432
- 数据库:gisdb
- schema:public
- 用户:postgres
- 密码:你设置的那个密码
填完之后可以先点一下"测试连接",如果看到"连接成功"的提示,再点保存。如果测试连接失败,大概率是JDBC驱动版本跟PostgreSQL版本不匹配。GeoServer自带了一个驱动,但版本比较老,连接PostgreSQL 15以上时经常报"Protocol violation"或者"Connection refused",解决办法是去PostgreSQL官网下载对应版本的JDBC驱动(通常是个postgresql-42.x.x.jar文件),放到GeoServer安装目录的WEB-INF/lib文件夹下,然后重启GeoServer。
4.2 图层发布与坐标体系设置
存储保存之后,GeoServer会提示你可以发布该schema下的空间表。点击"发布"进入图层配置页面。这里有几个关键字段需要手动确认或配置。
首先是"坐标参考系统"和"声明坐标参考系统"。如果数据入库时SRID设置正确,GeoServer能自动识别到EPSG:4326。如果它显示的是未知坐标系统,你需要手动填写EPSG:4326,或者直接在输入框搜索4326。这一步千万不能偷懒,坐标系统配错了,后面前端画出来的地图位置会偏到天上去。
其次是"数据范围"和"Lat/Lon边界框"。GeoServer通常可以根据数据库里真实的坐标范围自动计算,你可以分别点击"从数据中计算"和"从原生边界框计算"按钮,它会把经纬度范围自动填进去。横幅自动计算不出来的情况我遇到过几次,基本都是因为数据库里的geometry字段没有建立正确的空间索引,或者坐标系设置成了未知,排掉这两个问题就好了。
保存之后,这个图层就已经具备对外提供WMS服务的能力了。你可以在"图层预览"里找到它,点"OpenLayers"直接预览效果。正常看到地图上渲染出道路线,说明数据发布环节已经通过。
4.3 WMS请求结构拆解
WMS服务本质上就是一个HTTP接口,理解它的请求参数是调试和排错的关键。我以GeoServer预览页面实际发出的请求为例,拆开讲一下:
http://localhost:8080/geoserver/gisdemo/wms?service=WMS&version=1.1.0&request=GetMap&layers=gisdemo:roads&styles=&bbox=116.3,39.8,116.5,40.0&width=768&height=512&srs=EPSG:4326&format=image/pngservice=WMS:告诉GeoServer你要调用WMS服务。version=1.1.0:WMS协议版本号,1.1.0和1.3.0最常用,区别在于1.3.0版本在EPSG:4326下把bbox参数里的坐标顺序从"经度,纬度"改成了"纬度,经度",这是无数人踩过的深坑。request=GetMap:操作类型,GetMap表示获取地图图片,另外还有GetCapabilities(获取服务元数据)和GetFeatureInfo(获取要素属性)。layers=gisdemo:roads:你要请求的图层列表,多个图层用逗号分隔。bbox=116.3,39.8,116.5,40.0:当前视图的地理范围,顺序是minx,miny,maxx,maxy。width=768&height=512:输出图片的像素尺寸。srs=EPSG:4326:输出坐标系。format=image/png:输出图片格式,还有image/jpeg、image/svg+xml等。
为了验证这个接口是否正常,你可以在浏览器里直接访问这个URL,如果能显示一张地图图片,说明服务端一切正常。如果前端调不到、显示空白,先用这个方式把问题范围缩小到服务端还是客户端,排查效率会高很多。
工作区与图层的名称大小写也要注意。GeoServer对URL路径大小写敏感,gisdemo:roads和gisdemo:Roads是两回事。如果你在管理界面里看到图层名称是Roads,请求时写roads,就会返回400错误。这也是一个非常常见的低级错误。
5. 前端调用WMS服务的完整实现
5.1 OpenLayers加载WMS图层的两种写法
前端部分我直接用CDN方式引入OpenLayers,这样不需要本地搭建打包环境,复制代码就能跑。先写一个最基础的加载WMS图层的页面:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>WEBGIS WMS全栈演示</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@v9.0.0/ol.css"> </head> <body> <div id="map" style="width:100%;height:100vh;"></div> <script src="https://cdn.jsdelivr.net/npm/ol@v9.0.0/dist/ol.js"></script> <script> const wmsSource = new ol.source.TileWMS({ url: 'http://localhost:8080/geoserver/gisdemo/wms', params: { 'LAYERS': 'gisdemo:roads', 'TILED': true }, serverType: 'geoserver' }); const wmsLayer = new ol.layer.Tile({ source: wmsSource }); const map = new ol.Map({ target: 'map', layers: [wmsLayer], view: new ol.View({ projection: 'EPSG:4326', center: [116.4, 39.9], zoom: 10 }) }); </script> </body> </html>这段代码有几个点要提醒一下。ol.source.TileWMS会把当前视图范围分成若干瓦片,每一片单独发WMS请求,这样做的好处是不需要一次加载整张地图,性能更好。TILED: true这个参数用于告诉GeoServer按瓦片方式请求,如果去掉它,前端会变成单张全图请求,拖动时体验会差很多。serverType: 'geoserver'用来告诉OpenLayers按GeoServer的规则处理返回信息,特别是GetFeatureInfo请求时的参数格式。
如果你想让地图有层次感,可以在WMS图层下面叠加一个OpenStreetMap底图:
const osmLayer = new ol.layer.Tile({ source: new ol.source.OSM() }); const map = new ol.Map({ target: 'map', layers: [osmLayer, wmsLayer], ... });叠加时注意,如果底图是EPSG:3857(Web墨卡托),而WMS图层是EPSG:4326,两者坐标系不同,要么把底图换成同坐标系的切片,要么把view的projection改成3857,并用ol.proj.fromLonLat转换中心点坐标,否则两个图层无法对齐。
5.2 点击地图查询要素属性
WMS一个非常实用的功能是GetFeatureInfo——点击地图上的要素,查看它背后的属性数据。在OpenLayers里实现这个交互,只需要监听singleclick事件,拼出带INFO_FORMAT参数的请求地址,再用fetch拿到返回的JSON:
map.on('singleclick', function(evt) { const viewResolution = map.getView().getResolution(); const url = wmsSource.getFeatureInfoUrl( evt.coordinate, viewResolution, 'EPSG:4326', {'INFO_FORMAT': 'application/json'} ); if (url) { fetch(url) .then(resp => resp.json()) .then(data => { console.log(data); // 这里可以写一个弹窗展示属性字段 }) .catch(err => console.error(err)); } });执行逻辑是:用户点击地图后,OpenLayers把点击位置转换成地图坐标,结合当前分辨率生成一个GetFeatureInfo请求,GeoServer会返回点击位置所在要素的属性信息。INFO_FORMAT指定返回格式,application/json最方便前端处理,如果设置成text/html,GeoServer会返回一个属性表格页面,适合调试。
这里有一个提升体验的小技巧:GeoServer的GetFeatureInfo默认只查询预览范围内的要素,如果你点击的位置没有命中任何要素,返回的JSON里的features数组会是空数组,界面上不会有任何反应。所以最好加一个空数据提示,告诉用户"此处没有查询到要素",不要让人以为点击无响应。
5.3 用Leaflet快速验证WMS服务的兼容性
有人可能更熟悉Leaflet而不是OpenLayers,没关系,WMS是标准协议,切换到Leaflet只需要几行代码。Leaflet是轻量级地图库,API更简洁,特别适合做快速原型验证。
const map = L.map('map').setView([39.9, 116.4], 10); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(map); L.tileLayer.wms('http://localhost:8080/geoserver/gisdemo/wms', { layers: 'gisdemo:roads', format: 'image/png', transparent: true, version: '1.1.0' }).addTo(map);注意我在这里给WMS图层加了transparent: true,表示背景透明,这样底图能从WMS图层的空白区域透出来,这是叠加图层时几乎必填的参数。在OpenLayers里同样可以通过'TRANSPARENT': 'TRUE'来设置。
这一节想说明的核心观点是:只要服务端严格按照OGC标准发布WMS,客户端用什么库根本不影响数据层的发布逻辑。这也是为什么我特别建议学员先学明白WMS请求的本质,而不是死记某一个前端框架的API——框架会迭代,协议标准却是长期稳定的。
6. 常见问题排查与实操避坑
6.1 问题排查速查表
把这套流程从头到尾跑完,其实大概率会遇到几个经典问题。我整理成一张速查表,你在哪里卡住了直接对着查:
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| GeoServer连接PostGIS失败,提示Connection refused | 数据库端口未开放、服务未启动 | 确认PostgreSQL服务在运行,检查防火墙或host配置 |
| 连接超时,提示Unable to obtain connection | JDBC驱动版本不匹配 | 下载匹配PostgreSQL版本的postgresql-x.x.x.jar放到WEB-INF/lib |
| 发布图层后预览空白 | 坐标系设置错误或bbox没自动计算 | 重新设置EPSG:4326,点击"从数据中计算"生成bbox |
| 前端加载WMS后位置偏移 | 坐标系不一致,比如WMS是4326,底图是3857 | 统一坐标系,或者用fromLonLat转换坐标 |
| bbox请求返回400错误 | WMS版本1.3.0下EPSG:4326的bbox坐标顺序颠倒 | 显式用version=1.1.0,或者把bbox改为"纬度,经度,纬度,经度" |
| 要素属性中文变成乱码 | shp文件字符编码不是UTF-8 | 导入时用-W GBK或者-W UTF-8指定正确编码 |
| 浏览器控制台报CORS跨域错误 | 前端页面与GeoServer不在同一域名端口 | 让GeoServer支持CORS(web.xml配置),或用反向代理 |
| 图层预览正常,前端加载404 | 图层名称大小写不对 | 在GeoServer"图层"页面确认完整名称,例如gisdemo:roads |
其中跨域问题是纯前端本地调试时最常见、也最让人困惑的一个。因为OpenLayers从文件协议或localhost:5500这类端口发起请求,而GeoServer跑在8080端口,属于跨域请求。默认情况下GeoServer是允许CORS的,但如果你用的是Tomcat部署方式,可能需要在web.xml里手动添加CORS过滤器。这一块如果出现报错,在浏览器F12控制台里看Network标签,如果请求发出去了但Response里没有Access-Control-Allow-Origin头,基本就能确认是CORS问题。最简单的解决方案是用Nginx做一个反向代理,把/geoserver路径统一代理到8080端口,这样前端页面和API就同源了,自然就不存在跨域。
6.2 实操中积累的几个独门经验
除了上面速查表里的常规问题,我再说几个短时间内不太容易注意到的实操细节。
第一个是关于PostGIS空间索引的检查方法。很多人导入数据后以为-I参数已经帮忙建了空间索引,一切就万事大吉了,但实际工作中,如果数据量特别大,空间查询还是很慢,这时候可以去确认一下索引是否真的建出来了:
CREATE INDEX idx_roads_geom ON roads USING GIST(geom);GIST索引是PostGIS里最核心的空间索引类型,它能把空间范围查询从全表扫描变成索引查找,性能差距在百万级要素的数据集上是天壤之别。建议导入数据后显式执行一次,用到后面做空间分析时能省很多心。
第二个是关于GeoServer缓存目录。在GeoServer里添加了很多图层之后,工作目录会越来越大,特别是包含大量样式文件和地图切片缓存时。如果你发现GeoServer启动越来越慢,或者发布新图层后刷新不出来,可以在管理界面左侧菜单找到"文件"->"瓦片缓存",清理一下缓存。这条经验在长时间运行的服务器上特别实用。
第三个是请求日志的查看方法。当WMS请求返回的不是图片而是XML格式的错误信息(比如<ServiceExceptionReport>),不要慌,在GeoServer管理界面找到"关于与状态"->"日志",把日志级别调到"详细",然后重新发一次请求,它会直接给出Java堆栈信息,很多问题一眼就能定位。
第四个经验说一说"样式"这回事。WMS请求里经常会带一个styles=参数,它可以为空,也可以指定图层样式。GeoServer里通过"SLD"(Styled Layer Descriptor)定义样式,例如道路的线宽、颜色,行政区划的填充色等。如果你发布出来的图层颜色很不搭,可以在GeoServer的"图层"->选中图层->"发布"页面,找到"默认样式"下拉框,编辑或新建一个SLD来改变样式。用CSS方式定义SLD更简洁,但需要开启扩展,核心思路都是一样的,就是"把怎么画地图的规则告诉GeoServer"。
7. 扩展思路:这之后还能怎么玩
跑通基础链路只是一个开始,这套架构有非常多的扩展方向,我根据自己的项目经验给你指几个方向,方便下一步继续深入。
如果数据量巨大,WMS单张图片渲染变慢,可以升级到WMTS(Web Map Tile Service),把地图切成熟知的瓦片金字塔,GeoServer自带GeoWebCache服务,配置里的"切图"功能就能生成WMTS图层,前端调用方式也要相应换成ol.source.WMTS。这对百亿级栅格数据、全国范围影像浏览这类场景效果特别明显。
如果要做编辑业务,比如在地图上画地块、标注点,那就要引入WFS-T(Web Feature Service Transaction),它允许前端对要素数据进行增删改操作。GeoServer在"服务"菜单里可以启用WFS,前端通过ol.format.GeoJSON直接解析和上传要素,这个能力比WMS更进阶,但很实用。
如果要把系统做成一个完整的业务系统,前后端分离是必然的。前端除了OpenLayers,还可以加上Vue或React,GeoServer可以通过REST API来动态创建存储、发布图层,后端只需要维护用户权限和业务数据,把GeoServer当作一个独立的"地图服务微服务"来使用。这个架构我在实际项目里验证过,稳定性很高,可维护性也比单体应用好很多。
如果你想把地图服务和业务系统都部署到一台服务器上,可以用Nginx统一入口,把GeoServer、后端API、前端静态资源都配好反向代理,再用SSL证书开启HTTPS。到了这一步,你遇到的问题就不仅仅是GIS层面的了,还会涉及到服务运维、容器化部署这些通用后端知识。好在整套系统的层与层之间耦合非常低,你可以在任何一层单独做扩展,不必推倒重来。
最后说点我个人的体会。做WEBGIS全栈演示,最大的价值不是让你背下每一行代码,而是建立一个"数据从哪来、服务怎么转、前端怎么显"的全局心智模型。我第一次搭这套环境时,光折腾PostGIS扩展就花了一下午,后来慢慢发现,文档和教程里写的"直接下一步",实际操作中总是会冒出无数个版本号、路径、编码、端口的细节问题。这篇文章里的每一条注意事项,几乎都对应着我踩过的坑。你照着跑一遍,如果卡住了,多半是我经验里已经写过的某个坑;如果没卡住,那说明你比当时准备这篇内容的我顺手得多。这套组合现在依然是我做项目时的首选,希望你也能通过它,真正打通WEBGIS的任督二脉。