【GIS】GIS全栈工程师计划01———爆肝3w字带你彻底理解GIS

314 阅读48分钟

作者的小絮叨-------“GISer一定要知道自己在做什么”

GIS是一个集 空间数据存储管理空间数据分析决策空间数据可视化 的一个完全综合体。

GISer的核心价值在于知道如何根据数据的特点和地理环境选择和调整分析工具,而不是简单的调包侠。

首先容我大言不惭的修改了GIS的定义,特意强调了空间数据的地位。作为GISer,最重要的是什么?是制图?是算法?是模型设计?还是API设计与调用?但是这一切的基础是什么永远是数据,关键点就是:GISer面对的是空间数据而不是普通的数据(否则和计算机专业没有任何区别)因为空间数据涉及到地理,作为区域化变量,其数据结构、表示方法、存储管理、坐标系与投影、空间统计学与经典数理统计的差异,其遵循空间相关性(地理学第一定律),空间异质性(地理学第二定律),空间结构相似性(地理学第三定律)等等,这一切地学基础使得空间数据衍生出了自己的分析处理方法。而为了在信息化的今天,挖掘出空间数据最大的潜力以创造价值,需要一群同时拥有地学基础与信息化能力的人士,GIS诞生了。(好家伙,写到这里我自己都有点小激动?)

  • 现在网上对GIS的就业前景普遍是转码,转码,趁早转码,仿佛GISer以后的出路除了做最基本的数据处理,就是跟计算机专业的抢饭吃,但是伙计们,GISer的核心竞争力从来不是编程能力或算法实现,而在于对空间数据的深度理解。即便很多工具或包能提供现成的分析方法(如插值),但理解何时使用这些工具如何调整参数、以及数据背后的空间特性,这才是GIS开发人员与一般程序员最大的区别。咱就以“有手就行”的空间插值为例吧,大家都会插值,只要有数据点,调个包而已,但是什么时候调IDW,什么时候用RBF,什么时候用地统计学Kriging家族,各种现成的包的参数怎么针对手中的数据与当地的地理条件进行合理设置?假设你听别人说Kriging是BLUE(无偏最优估计),所以你选择了Kringing,那么手里的数据你了解吗?这些数据遵循区域化变量的随机性和结构性么?在使用克里金的时候有没有考虑它符合正态分布么?需要先进行全局趋势去除么?选择普通克里金、泛克里金还是协同克里金?什么是本征假设和二阶平稳假设?均值漂移又是什么?半变异函数模怎么建立,基台值块金值变程又代表了什么呢?...... 没错,这仅仅是一个空间插值而已,当然你如果说,我作为GIS专业的人我也不会,伙计,这不是GIS的问题,这是你的问题。如果你真的只会用桌面端GIS软件进行数据处理与制图,那你真的要好好考虑下了,这种工作路边拉个人培训一周都能干,我没有危言耸听。如果你自认为没有计算机能力的同时也没有地理学基础,也别慌,考公考编,上岸毕竟是最终归宿对吧。
sequenceDiagram
    participant User as 用户需求
    participant GIS as GIS应用
    participant DB as 空间数据库
    
    User->>GIS: ①请求温度插值分析
    GIS->>DB: ②查询气象站位置和温度数据
    DB->>GIS: ③返回查询结果
    GIS->>GIS: ④最优插值算法逻辑<br>(你的领域)
    GIS->>User: ⑤返回插值结果
    GIS->>DB: ⑤也可以存储插值结果

做个总结吧:GIS = 地学理论储备 + 计算机能力 ——> 处理空间数据 ——> 结果与服务。 其中地学理论是GIS相较于计算机科班的优势。计算机能力则是GIS优于人文自然地理,城乡规划,地理科学等姊妹专业的地方。你就是国家设立该学科时所愿的“融合性学科培养出的综合性人才”(手动狗头🐶)。

2024-10-17-周日凌晨1:00🌛



🌕第一章:GIS基石——空间数据

空间数据是对现实世界的抽象表示,可理解为一种包含了位置、形状、关系、该位置上具有的属性...的一种数据结构。简单的数值类型,如浮点布尔这种,是无法表示出一个空间对象的。首先说位置,空间数据不仅是靠坐标表示位置,且这种位置是建立在坐标系上的,同一组坐标在不同的参考系统中可能表示完全不同的位置。再来看形状,一个湖泊可能由数百个坐标点共同组成;一幢大楼不仅仅有二维平面占地,还有高度,甚至包含时间维以展示建造过程,因此这种多维度特征与基本数据类型的一维形成鲜明对比,也正是由于空间数据的多维特性,海量且复杂,所以空间数据也有专属的索引结构即空间索引,当索引到数据后,可以对空间数据进行空间操作和分析,如计算两个几何对象的交集、判断一个点是否在多边形内、计算最短路径等,这些操作在基本数据类型中是很难实现的。

拓扑关系补充

拓扑关系是空间数据之所以能进行空间分析的核心之一。矢量的拓扑关系是显式的,通常用额外的数据结构如拓扑表记录,一个拓扑表可以记录每个多边形的边界线段,以及这些线段如何连接形成多边形,大多数复杂的空间分析都基于矢量的。与矢量不同,栅格数据的拓扑关系是隐式的,是基于像素的位置和值,也可以进行某些类型的拓扑分析,如邻域分析,例如要确定两个像素是否相邻,可以通过检查它们的坐标位置来判断。要确定一个区域的连通性,可以通过连通域分析算法来实现。

拓扑关系能做什么?

  • 1、数据完整:可确保多边形不重叠,线段相连等
  • 2、高效查询:提高空间查询的效率。例如直接找出所有相邻的区域。
  • 3、空间分析:很多高级空间分析操作都基于拓扑关系,如网络分析、流向分析等。
  • 4、数据压缩:拓扑结构允许更高效的数据存储,因为共享边界只需存储一次。
  • 5、数字孪生:在城市路网建模时,需要明确哪些道路是相连,哪些是相交的。
  • 这里仅是绘制了一个简单的空间关系图,感兴趣的搜索关键词:九交模型
graph TD
    A[相等Equals] --- B[不相交Disjoint]
    A --- C[相交Intersects]
    C --- D[接触Touches]
    C --- E[跨越Crosses]
    C --- F[内含Within]
    C --- G[包含Contains]
    C --- H[重叠Overlaps]


🌕第二章——空间数据的管理

普通的RDBMS可以存储坐标数据,但无法直接进行复杂的空间操作和分析。空间数据库是数据库在空间领域的延伸,它仍是基于普通数据库的表&行形式,但增加了空间数据类型相应的处理方法高效的空间索引

  1. 增加数据类型:空间数据库支持直接存储和操作几何对象。
  2. 增加功能函数:空间数据库提供了大量专门的函数来处理空间关系和进行空间分析。
  3. 增加空间索引:空间数据库使用特殊的索引结构和算法来优化空间查询。
  • 我们来具象化一下,以存储POI为例比较这两种数据库的存储方式,再进行一个简单的查询。
一、普通关系型数据库的建表存储POI
CREATE TABLE poi (
    id INT PRIMARY KEY, -- 主键,唯一标识每一个地点
    name VARCHAR(100), --地名,限制为最多100个字符
    latitude FLOAT,   
    longitude FLOAT,  
    type VARCHAR(50)  
);

======================================
| id |      name      | latitude   | longitude  | type        |
=====================================
| 1  | 汇金中心       | 40.7829  | -73.9654  | 写字楼    | 
| 2  | 凤凰世嘉       | 40.7484  | -73.9857  | 大型商圈 | 
| 3  | 利群广场       | 40.7580  | -73.9855  | 商超广场 | 
=======================================


二、空间数据库的建表存储POI
    --latitude FLOAT,   
    --longitude FLOAT,  
   --替换为↓ 
    location GEOMETRY(POINT, 4326),
   
==========================================
| id |   name       |           location                        | type        |
===========================================
| 1  | 汇金中心   | POINT(-73.9654 40.7829)     | 写字楼    |
| 2  | 凤凰世嘉   | POINT(-73.9857 40.7484)     | 大型商圈 |
| 3  | 利群广场   | POINT(-73.9855 40.7580)     | 商超广场|
===========================================
  • 普通数据库与空间数据库查询POI
一、普通数据库的查询
     SELECT * FROM poi 
     WHERE latitude BETWEEN 40.70 AND 40.80 
     AND longitude BETWEEN -74.00 AND -73.95;
     
二、空间数据库的查询
     SELECT * FROM poi 
     WHERE ST_Within(location, 
       ST_MakeEnvelope(-74.00, 40.70, -73.95, 40.80, 4326)
     );
     -- ST_MakeEnvelope()函数直接创建了一个矩形框范围,4326是WGS84

通过上述例子,可见传统数据库虽然能通过普通的列来存储纬度和经度信息,但无法认识到这些位置信息作为一个整体对象,数据只是孤立的普通数据,可以按照基本数据的方式在这些列上创建普通索引,但在进行涉及坐标范围比较的查询时效率低。空间数据库则使用特殊的几何类型GEOMETRY来存储点位信息,并且数据库能够将一组坐标视为组成单个对象的一部分。写到这里,空间数据和普通数据应该讲的够清晰了,那么就以一个表格来总结吧。

特征空间数据类型基本数据类型空间数据库关系型数据库
维度多维(2D, 3D, 4D)一维--
结构复杂(多个点组成)单一值--
表示几何(点线面体)数值&字符--
运算涉及空间运算
(如相交、包含)
算术运算
逻辑运算
--
索引--空间索引
(R树、四叉树、网格)
B树、哈希索引等
可视化直接在地图上可视化需要额外处理--
坐标系需要不需要内置多种坐标系统和投影-
拓扑关系支持不支持能够存储对象间的拓扑关系-
数据类型--额外支持空间数据类型基本数据类型
空间查询--支持空间查询
(如包含、相交、距离等)
-
空间分析--支持空间分析
(如缓冲区分析、叠加分析)
-
查询语言--扩展的SQLSQL
数据格式--支持各种空间数据格式
(如shp、GeoJSON)
CSV、XML等通用格式
3D支持--部分空间数据库支持不支持3D数据

接下来看看都有哪些形式的空间数据库

  • 方案一:原生数据库拓展

graph TD
    RDBMS[主流数据库] --> PG[PostgreSQL]
    RDBMS --> ORA[Oracle]
    RDBMS --> MSSQL[Microsoft SQL Server]
    RDBMS --> MYSQL[MySQL]
    RDBMS --> Apache[Apache Spark]
    PG --> POSTGIS[PostGIS<br>开源]
    ORA --> ORASP[Oracle Spatial and Graph<br>企业级]
    MSSQL --> SQLSP[SQL Server Spatial<br>企业级]
    MYSQL --> MYSP[MySQL Spatial<br>轻量级]
    Apache --> aaa[GeoSpark<br>大数据\分布式]
    
  • 方案二:Esri推出的SDE引擎 :比如ArcSDE for SQL Server ; ArcSDE for PostgreSQL ; ArcSDE for Oracle。那么 SDE (Spatial Database Engine) 是什么?SDE提充当了ArcGIS软件和各种关系型数据库之间的中间层,提供了统一的接口规范,使ArcGIS能够与不同的数据库系统交互。但SDE可是叫:“空间数据引擎”,如果仅把它当做是WSGI这种协议接口就小瞧它了,它能把普通数据库直接变为空间数据库。原理是在原本的数据库中创建额外的表来存储空间数据的元数据、坐标系、存储点、线、多边形等几何对象,提供了丰富的空间操作函数空间索引,支持空间数据的版本控制,支持ArcGIS的各种工具和功能,同时也提供了API和SDK,允许开发者扩展和定制空间数据处理功能。

还是不够具象化?那就看看空间数据库能做什么,怎么做(不懂SQL继续看没事,明白大致原理就行)

1.创建新表                   🍖GEOMETRY(点、线、面)🍖
CREATE TABLE poi (id INT, location GEOMETRY(POINT));
------------------------------------------------------------------

2. 创建空间索引           🍖GIST()🍖  一种多维索引,支持R树等索引结构
CREATE INDEX idx_location ON poi USING GIST (location);
------------------------------------------------------------------

3. 空间关系判断            🍖ST_拓扑关系()🍖,
SELECT * FROM areas WHERE ST_Intersects(geom, 'POINT(0 0)');
SELECT * FROM areas WHERE ST_Contains(geom, 'POINT(1 1)');
------------------------------------------------------------------

4. 空间测量                   🍖ST_计算几何()🍖
SELECT ST_Distance(a.geom, b.geom) FROM a, b;
SELECT ST_Area(geom) FROM polygons;
SELECT ST_Length(geom) FROM lines;
------------------------------------------------------------------

5. 空间构造                    🍖ST_MakePoint()🍖
INSERT INTO poi (location) VALUES (ST_MakePoint(0, 0));
------------------------------------------------------------------

6. 坐标转换                     🍖ST_Transform()🍖
SELECT ST_Transform(geom, 4326) FROM spatial_table;
------------------------------------------------------------------

7. 空间分析        
SELECT * FROM poi ORDER BY location <-> 'POINT(0 0)' LIMIT 5; 最近邻分析
SELECT ST_Union(geom) FROM polygons;   聚合
SELECT ST_Buffer(geom, 50) FROM lines; 缓冲区
SELECT * FROM pgr_dijkstra('SELECT id, source, target, cost FROM edges',1, 5); 迪杰斯特拉
--虽然PostGIS扩展了SQL的功能,使其能够处理空间数据,但它的核心仍然是数据管理和查询。
--适合进行基础的空间分析,如缓冲区、最近邻分析、空间交集等。
--对于MGWR、莫兰指数、插值等高级空间统计分析,通常需要专业的GIS软件或外部工具来实现。
------------------------------------------------------------------

8. 栅格操作                  🍖ST_MapAlgebra()栅格代数运算 🍖
SELECT ST_MapAlgebra(rast1, rast2, '[rast1] + [rast2]');
------------------------------------------------------------------

9. 拓扑操作    
SELECT ST_Simplify(geom, 0.1) FROM complex_shapes; 简化顶点
ST_VoronoiPolygons()
ST_Intersection()
------------------------------------------------------------------


🌕第三章——WebGIS的地图服务

mindmap
  root((WebGIS))
    (前端开发)
      [地图渲染]
        矢量图层
        栅格图层
        数据可视化
      [用户交互]
        地图控件
        事件处理
        响应式设计
      [技术栈]
        JavaScript框架
        地图库
        WebGL
    (后端开发)
      [空间数据管理]
        数据存储
        数据处理
      [GIS服务器]
        GeoServer
        MapServer
        QGIS Server
      [空间分析]
        矢量分析
        栅格分析
        网络分析
      [技术栈]
        Python
        Java
        Node.js
    (数据)
      [数据格式]
        矢量
        栅格
      [数据源]
        开放数据
        商业数据
        遥感数据
      [数据处理流程]
        获取
        清洗
        转换
        存储
        优化
    (服务集成)
      [OGC服务]
        WMS/WMTS
        WFS
        WPS
      [第三方API]
        地理编码
        路径规划
        POI搜索
    (部署与运维)
      [容器化]
        Docker
        Kubernetes
      [云服务]
        AWS/GCP/Azure
      [性能优化]
        缓存策略
        负载均衡
        CDN
    (开发流程)
      需求分析
      原型设计
      开发实现
      测试
      部署
      维护
    (安全与合规)
      [身份认证]
      [数据加密]
      访问控制
      隐私保护
    (新兴技术)
      [实时GIS]
      [大数据分析]
      [机器学习集成]
      WebAR/WebVR

在熟练掌握了空间数据与分析后,你已经是一个入门的GIS工程师了,但是时代的车轮是滚滚向前的,互联网技术的发展使得地理空间数据不再局限于本地系统,可以通过Web服务进行远程访问和共享。Web地图服务就是在这个背景下诞生的,它提供了一种标准化的机制,使得地理空间数据可以在不同的系统之间交换和使用,并提供了各种功能以满足不同的地理数据需求(可以简单的理解为前后端分离架构中的api接口,)。So,地图服务只是一种基于网络来提供的GIS服务的技术,核心依然是GIS。

graph TB
    subgraph "🗄数据层"
    A[空间数据库<br>PostGIS] --> |存储| B[文件系统<br>栅格\矢量]
    end
    
    subgraph "🖥服务层"
    C[GIS服务器<br>QGIS Server] --> |读取数据| A
    C --> |提供服务| D[OGC制定的各种地图服务协议]
    end
    
    subgraph "🌐应用层"
    G[Web应用<br>Vue+Django] --> |调用服务| D
    H[客户端地图引擎<br>Cesium,Leaflet] --> |渲染| G
    end
    
    I[用户] --> |交互| G

想象楼下开了家饭店,涉及的角色有顾客、服务员、厨房和菜单。如果没有菜单,顾客直接跑到厨房问厨师有什么吃的。厨师需要一遍遍解释有哪些食材、可以做哪些菜。顾客如果又忘了他想吃的菜名,他就记得那个玩意儿甜甜的咸咸的脆脆的,他说他来到唐山吃过最好吃的东西,总之描述了半天,厨师才知道他想吃的菜叫鸭黄豆角。这就像前端直接访问GIS服务器:每次请求都需要大量沟通,而且用户可能说不清想吃啥,即不懂如何正确请求地理数据,耽误正事。而且直接访问“厨房”(GIS服务器)可能暴露敏感信息,比如偷取了价值10万的卤大肠秘方!而GIS服务器需要处理大量琐碎请求,而非专注于数据处理,就像厨师就该做饭,不负责接单。假设有菜单,顾客查看菜单了解有哪些选择,服务员接收订单,用标准化的方式传达给厨房:“牛排7分熟然后一杯Venti摩卡星冰乐放一包混合果仁果脯进去搅拌然后装在两个Tall杯里~”。厨师可以专注于做东西,餐厅可以轻松更新菜单,增加新菜品,而不影响厨房操作。这里地图服务就是菜单。

地图服务是一套基于标准协议的接口API,这些协议定义了如何请求和传输地理空间数据。地图服务通过HTTP提供访问,类似于RESTful API,但要遵循OGC制定的协议。客户端应用发送HTTP请求到地图服务API,然后地图服务解析后将请求路由到GIS服务器,GIS服务器调用相应的空间分析功能执行,处理后的数据按照地图服务协议格式化,最后格式化的响应通过HTTP返回给客户端。

普通API、RESTful API 、OGC地图服务API

类型示例说明
普通APIGET https://api.example.com/ getLocation? building=汇金中心C• 在URL中 使用动词
• 参数通过查询字符串传递
• 返回建筑的基本信息
RESTful APIGET https://api.example.com/buildings/汇金中心C• 使用名词和资源标识符
• 利用HTTP方法语义
• 可能返回更详细的信息
地图服务 (WMS)GET https://mapservice.example.com/wms?<br>SERVICE=WMS&VERSION=1.3.0&...省略一大堆='汇金中心C'• 使用标准化的参数
• 返回地理数据
• 支持地理特定的参数
• 可请求特定图层、范围和分辨率,使用过滤条件
GET https://mapservice.example.com/wms?   🌀指定HTTP请求的方法和目标URL。
SERVICE=WMS  🌀指定请求的服务类型为WMS,告诉服务器这是一个WMS请求。
VERSION=1.3.0  🌀指定WMS服务的版本。
REQUEST=GetMap  🌀指定请求的操作类型为`GetMap`,告诉服务器需要获取地图图像。
LAYERS=buildings  🌀指定要请求的图层名称,告诉服务器需要`buildings`图层。
CRS=EPSG:4326  🌀指定坐标参考系统为WGS84坐标系EPSG:4326
BBOX=121.47,31.23,121.48,31.24  🌀指定请求的地图区域的边界框,四个值为左下右上矩形区,包含汇金中心C的位置。
WIDTH=800  🌀指定返回地图图像的宽度(以像素为单位)
HEIGHT=600  🌀指定返回地图图像的高度(以像素为单位)。
FORMAT=image/png  🌀指定返回地图图像的格式。
CQL_FILTER=name='汇金中心C'   🌀指定过滤条件,这里使用CQL过滤条件`name='汇金中心C'`

上面展示了一次完整的WMS地图服务所包含的参数,那么不同的地图服务就是不同请求参数的API?因为每种地图服务(如WMS、WFS、WCS、WMTS)都是为了处理特定类型的地理数据而设计的,所以有自己独特的请求格式和响应格式?是这样的,接下来就来看看这些不同的地图服务的请求参数不同在哪里吧。

特征WMTSWMSWFSWCS
用途提供预渲染的地图瓦片提供地图提供矢量数据提供栅格数据
主要参数LAYER(请求的图层名) TILEMATRIXSET(矩阵集) TILEMATRIX(矩阵级) TILEROW(瓦片行索引) TILEROL(瓦片列索引) FORMAT(输出格式)LAYERS
BBOX(边界框矩形) WIDTH(返回地图宽) HEIGHT(返回地图高) FORMAT
CQL_FILTER(过滤条件)
TYPENAME
BBOX
FILTER
OUTPUTFORMAT
COVERAGEID(ID)
SUBSET(裁剪范围)
FORMAT
图像尺寸-WIDTH=800
HEIGHT=600
--
过滤条件-CQL语法的过滤条件XML格式的过滤-
瓦片索引TILEROW=0
TILECOL=0
---
返回数据类型PNG、JPEGPNG、JPEG、GIF、PDFGML、GeoJSON、Shapefile、KMLTIFF、NetCDF、GeoTIFF、HDF

关于图像尺寸:WMS支持动态生成地图图像,能够根据用户指定的区域(ROI)和其他参数灵活调整图像大小和比例尺,提供定制化的地图体验。相比之下,WMT服务返回的是预先生成的固定尺寸和比例尺的地图瓦片,并按照不同的缩放级别进行组织成金字塔结构缓存起来,因此无法动态更改图像大小,主要用于提高加载速度和性能。WFS则直接传输矢量地理要素数据,而非渲染后的图像,因此不存在图像尺寸的概念,用户可以对这些原始数据进行操作和编辑,并在修改后将新要素重新添加回数据库。最后,WCS返回栅格数据,用户通过指定COVERAGEID和SUBSET参数请求特定的数据,其尺寸由数据本身或裁剪范围决定,因此同样不支持动态更改大小(对比一下,WMS返回的是栅格图像适合地图展示,而WCS返回的是栅格数据本身,适合用于数据下载和分析)。(COVERAGEID(ID)是一个包含多幅图像或多种数据类型的整个数据集,假设你知道唐山的卫星影像数据集的ID是tangshan_satellite_image,然后定义唐山的具体区域。西南角(南纬:38.0°,西经:117.0°)东北角(北纬:39.0°,东经:118.0°)将这个范围通过SUBSET参数传递给WCS,就能得到这个区域的影像数据)

关于过滤条件:WMS和WFS支持过滤,因为需要根据属性选择ROI区域与特定要素,WMTS提供预先渲染的瓦片,也不支持动态过滤。WCS通常不支持基于属性的过滤,因为栅格本身是一个个像元,其值就是像素值,不像矢量一样作为对象具有具有属性概念。

坐标参考系统和地理范围:所有这些服务都支持坐标参考系统和地理范围的概念,但它们的表达和实现方式各有不同。WMS和WFS通过传统的边界框(BBOX)进行地理范围的定义,而WMTS则通过瓦片矩阵集进行索引。WCS则结合了这些元素,允许用户通过COVERAGEIDSUBSET来精确访问栅格数据。

WebGIS服务的性能问题怎么解决呢?

  • 多级缓存:在WMS和WMTS中设置多个缓存层来提高地图加载速度。①服务器端缓存:系统首先检查是否已经缓存,有缓存就直接返回不需要再渲染了。②CDN缓存:将缓存的图像存储在靠近用户地理位置的CDN节点上,从而减少传输时间。
  • 空间索引:在WFS中使用空间索引加速查询。
  • 分布式计算:在WCS和WPS中使用分布式计算和数据分片技术来处理大规模栅格数据。比如Apache Spark可以在处理全球气温数据时,将数据分散到多个节点上并行处理。每个节点处理不同区域的数据,这样大大缩短了整体处理时间。
  • 切片:切片地图服务的请求直接返回文件,无需执行服务器端代码,使得响应快速。例如,请求一个特定缩放级别和位置的PNG图片,服务器仅检索文件并返回,不涉及Web服务逻辑。这种简单的文件请求是Web GIS快速显示地图的关键。即,WMS是动态的(涉及到请求到GIS服务器),WMTS是静态的(请求只到缓存服务器即可不需要运行逻辑)我们再来类比Web开发,其中Web服务器将静态动态转发给Djnago服务器,将静态请求转发给静态存放服务器(一般就是自己))

可将地图绘制的原理归类为三大类型:动态地图绘制服务切片地图绘制服务矢量数据绘制服务

  1. 第一种WMTS:预先生成的地图切片,服务器发送给浏览器服务器事先将地图按照不同缩放级别和区域生成图像切片,浏览器请求时,服务器直接发送这些预先生成的切片。因为不需要每次动态生成地图图像,尤其适合高并发请求。缺点是只反映切片生成时的数据状态,地图内容不随数据的更新而变化,除非重新生成切片。除了最常见的WMTS,还有XYZ瓦片服务,它也是基于切片的服务,但更简单,没有WMTS的标准化请求参数规范,广泛用于各类在线地图(如OpenStreetMap、Google Maps等)。TMS(Tile Map Service) 也是一种切片服务,但请求方式稍有不同,它以标准的网格来组织切片。
  2. 第二种WFS:服务器返回空间图形与属性的矢量数据,由浏览器端负责渲染服务器返回的是矢量数据(如Shapefile或GeoJSON格式的点、线、面要素及其属性),不需要服务器绘制,浏览器中的GIS框架可以在客户端自行渲染这些数据。因此你作为用户,可以自行对数据进行交互和在线编辑,甚至执行一些简单的空间分析逻辑。但如果传输数据量可能较大,尤其在数据复杂或大范围时,客户端渲染负担较重,这就与你自己的电脑配置有关了哦。
  3. 第三种WMS:服务器动态生成地图图像,返回给客户端由服务器根据客户端的请求参数(如地图范围、图层、样式等)并返回给浏览器,由于是根据你的请求绘制的,因此能够实时反映最新数据,适合需要频繁更新或动态数据的场景。适合展示实时气象地图、交通流量监控这类即时性的地图绘制,这类不要用切片。

那么这些服务API的典型场景有哪些呢?帮你想了一些最常用最典型的应用场景,我们分别以一个普通用户和一个GIS用户的视角来看看日常操作中,哪些高频操作用到哪些服务吧

操作场景普通用户GIS用户商业API
查看地图浏览周边街道(WMTS加载底图)加载多层图层(WMTS底图,WMS专题图层)AMap.Map 创建地图实例
AMap.Marker 添加标记
路线规划规划路线(依赖内置路径规划服务)分析交通网络(WFS获取道路数据,WMS可视化)AMap.Driving 驾车路线规划
AMap.Walking 步行路线规划
查找地点搜索餐馆或加油站(基于WMTS底图)商业选址分析(WFS获取POI数据,WMS热力图)AMap.PlaceSearch 地点搜索
卫星图像切换卫星视图(使用WMTS影像)土地利用分析(WCS获取原始卫星数据)无直接卫星影像API,通常使用底图替代
查看地形查看目的地3D地形(使用WMTS地形图层)地形分析(WCS获取DEM数据)无直接地形API,可能用AMap.TileLayer集成其他层
天气查看查看天气和雷达图(基于WMTS)制作专题天气图(WCS气象数据,WMS可视化)无直接天气API,通常与其他服务结合使用
房产信息查看房屋位置(使用WMTS底图)房地产市场分析(WFS获取房产数据,WMS展示图)AMap.PlaceSearch 结合房产数据

WMTS使用最广泛,特别是在需要快速加载大范围底图的场景中,普通不知道他们正在使用WMTS,但大多数现代地图应用都在后台使用这项技术。WMS由于在动态生成地图很有用,因此GIS专业用户可能会更频繁地直接使用WMS,特别是在需要自定义地图样式时。WFS主要被GIS专业用户使用,因为它提供了对原始矢量数据的访问,允许进行复杂的空间分析和查询。WCS同样主要被GIS专业用户使用,因为普通的PNG不适合进行栅格分析,但是很多空间分析都是需要用栅格计算,当然许多复杂的操作可能涉及多个服务的组合使用,例如使用WMS显示数据,同时使用WFS进行属性查询

某些特定功能(如路径规划、地理编码)可能需要额外的专门服务,这些服务可能基于但不限于OGC标准,完了,这句话好像又埋了一个坑...怎么还能不限于OGC标准呢,自己还能造标准?首先直击本质:OGC提供的地图服务就是几个实现不同基础功能的API,由于功能不同,自然请求的参数也不同,那么厂商提供的地图服务自然也是实现某种复杂的API,【复杂体现在,它不是OGC的这些基础功能,而是直接绑定了该厂商GIS服务器端里写好的某种特定功能的逻辑】,因此能够一步到位地完成复杂功能调用,而不是像OGC一样靠这几个服务API通过组合并在GIS服务器里添加自己祥泰的GIS逻辑来实现复杂需求。比如进行交通的路径规划功能时,商业API可能只需一个简单的map.getRoute(骏兴园, 汇金中心)调用就行,而使用OGC服务则需要一系列步骤:首先使用WFS获取道路网络数据,然后通过其他服务获取实时交通信息,接着在服务器端实现路径规划算法并整合交通数据进行优化,最后使用WMS生成结果地图。假设一个完整的应用是一套LEGO的霍格沃茨城堡叭,高德的API就是一个个的套件,比如格兰芬多休息室,哈利人偶,一个拼好的魁地奇球场,而OGC则是最最基本的那几种积木零件,一个休息室也需要一个个拼接起来。但是这样的好处就是,我可以给哈利换成马尔福的身体,给格兰芬多墙壁换成斯莱特林的绿色,甚至可以用零件直接拼出指环王的刚铎与白城,再送个赛博朋克荒坂大厦魔到霍格沃茨旁边。相比之下,直接使用预制的大组件不能实现自定义,但是确实可以快速组合这些大型部件来创建应用。可见OGC服务需要了解每种OGC服务的用途和特点,要不然你都不知道怎么组合成一把椅子,更别说上述天马行空的想法了,当然只了解这些请求是不够的,它们只是数据请求传输的协议而已,你得有空间数据处理和分析与算法能力,服务器端GIS软件的配置和管理能力。第二种就是“调包侠”,你知道这个API能干啥、怎么调用、以及基本的Web知识就差不多啦,别看不起,其实谁不是调包侠呐~,总之通过处处调API,其实你的应用该有的功能也都会有的。

那么就开始模拟一个简化的实战吧,自己搞个路径算法API ? 使用OGC的第一步自然是选择积木(根据需求挑选适当的OGC服务),然后将不同服务组合在一起,并且在服务器上实现GIS逻辑和算法。下面是超级简化版,比如参数省略了很多,目的是展示思想,我以后会出一个系列,手把手教大家从零搭建自己的WebGIS项目。

【OGC】

1️⃣使用WFS获取道路网络数据
road_network = get_wfs_data("http://gis-server.com/wfs", {
    "service": "WFS",
    "request": "GetFeature",
    "typeName": "roads"
})

2️⃣设计路径规划算法
def custom_routing(start, end, network):
    # 自己写,或者用使用开源库如OSR
    return calculated_route

3️⃣使用WMS获取底图,将上述路径叠加上来
basemap = get_wms_map("http://gis-server.com/wms", {
    "service": "WMS",
    "request": "GetMap",
    "layers": "basemap"
})

4️⃣整合结果
display_route(basemap, custom_routing(start, end, road_network))
【第三方商家】

1️⃣创建地图实例
var map = new CommercialMap('map-container', { key: 你的秘钥'  });

2️⃣使用内置的路径规划服务
map.getRoute({                                       「🤔为什么说厂商提供的:map.getRoute()是个黑盒?」
    start: [longitude1, latitude1],         1、不知道它使用了什么地图数据(可能是卫星图像、矢量地图等)。
    end: [longitude2, latitude2],          2、不知道它用了什么路径规划算法(可能是Dijkstra、A*或其他更复杂的算法)。
    onResult: function(route) {            3、不知道它是怎么考虑交通状况的、道路施工等实时因素。
        map.displayRoute(route); }});    4、就连渲染方式(如路径的颜色、宽度、动画效果等)也是预设的。       
 
graph TD
    User[用户]
    FrontEnd[高德地图前端GUI]
    GISServer{GIS服务器}
    GeoCoding[地理编码服务]
    RouteEngine[路径规划引擎]
    TrafficData[实时交通数据服务]
    RouteOpt[路线优化算法]
    SpatialDB[(空间数据库)]

    User -->|1. 输入起点和终点| FrontEnd
    FrontEnd -->|2. 发送路线请求| GISServer
    GISServer -->|3. 地理编码请求| GeoCoding
    GeoCoding -->|4. 返回坐标| GISServer
    GISServer -->|5. 路径规划请求| RouteEngine
    RouteEngine -->|6. 查询路网| SpatialDB
    SpatialDB -->|7. 返回路网数据| RouteEngine
    RouteEngine -->|8. 返回初步路径| GISServer
    GISServer -->|9. 请求交通数据| TrafficData
    TrafficData -->|10. 提供交通信息| GISServer
    GISServer -->|11. 路线优化请求| RouteOpt
    RouteOpt -->|12. 返回优化路径| GISServer
    GISServer -->|13. 返回最终路线| FrontEnd
    FrontEnd -->|14. 显示路线| User

文章至此,你应该了解了WebGIS的运行原理了,包括数据怎么存储管理,怎么用,怎么传输等等,其实文章缺少了一部分,就是数据渲染,没有人会拿到一堆JSON在那里做分析,因此引出本文最后的章节。

🌕第四章——WebGIS渲染分工

在现代WebGIS应用程序中,前端渲染侧重于用户交互和实时渲染,依赖于客户端的计算能力;而后端则负责复杂的数据处理和生成静态图像,从而减轻客户端的负担,因此主流做法为:常见做法包括使用后端渲染的底图,并在前端添加交互式图层。以构建一个交互式的全球天气地图应用程序为例,初始页面加载时,服务器预先渲染基础地图界面,包括地图容器、控制按钮和基本的用户界面元素。这一过程在Web开发中相当于使用Django模板系统来渲染初始HTML页面。服务器还会预先生成一些基础地图图层,如国界和主要城市,这些图层作为静态或半静态的瓦片地图存在,类似于模板层预先生成的静态内容,如网站的页脚和关于页面等不经常变化的部分。在用户访问该页面后进行操作时,系统不需要再次渲染整个页面,而是采用前端渲染技术(如Mapbox GL JS或OpenLayers)动态加载和显示实时天气数据,例如温度和降水量,只更新交互部分。这种动态更新在Web开发中相当于使用AJAX或Vue.js来实时更新页面内容,如用户的消息通知。由于AJAX技术的应用,用户能够平移、缩放地图并切换不同的天气图层,这些操作均由前端JavaScript库处理,从而提供流畅的用户体验,而不必每次向后端请求新的URL来重载页面,这种设计将天气应用程序构建为单页面应用(SPA),使得前端框架能够处理交互以避免页面刷新。当然,复杂的天气模型计算(例如预测未来48小时的天气)仍然需要在服务器端完成,计算结果会作为处理后的数据或预渲染的图层发送到前端。这是因为这种大规模的运算推理对于前端框架(如Vue.js)而言较为复杂,同时海量数据存储在后端,也不适合每次都从后端通过JSON获取数据并在浏览器中反复计算。总之,这种混合方法在WebGIS和一般Web开发中都能提供良好的平衡,既能实现快速的初始加载,又能支持流畅的用户交互,以及处理复杂数据的能力。这种灵活的架构使开发者能够根据具体需求在前端和后端渲染之间进行选择,从而优化性能和用户体验。

graph TD
    subgraph Django MVT
        A1[用户请求网页<br>Web浏览器] --> B1[Django处理请求\nDjango, Gunicorn, Nginx]
        B1 --> C1{是否为首次加载?}
        C1 -->|是| D1[视图处理数据\nORM->MySQL]
        D1 --> E1[使用模板渲染HTML\nTemplates]
        C1 -->|否| F1[使用AJAX请求数据\njQuery \ Axios \ Fetch API]
        E1 --> G1[返回完整HTML页面\n]
        G1 --> H1[用户看到页面\nWeb浏览器]
        F1 --> I1[视图处理并返回JSON数据\nDRF]
        I1 --> J1[JavaScript更新页面内容\nReact\ Vue.js\ Angular]
        J1 --> H1
    end
    
    subgraph WebGIS
        A2[用户请求地图\nWeb浏览器] --> B2[GIS服务器处理请求\nGeoServer, MapServer]
        B2 --> C2{是否为首次加载?}
        C2 -->|是| D2[加载基础地图数据\nPostGIS]
        D2 --> E2[生成初始地图图像或瓦片\nMapnik, GDAL]
        C2 -->|否| F2[使用AJAX请求数据\nOpenLayers, Leaflet]
        E2 --> G2[返回地图HTML和JavaScript]
        G2 --> H2[用户看到地图\nWeb浏览器]
        F2 --> I2[GIS服务处理并返回地理数据\nGeoJSON, KML]
        I2 --> J2[客户端JavaScript更新地图\nMapbox, ArcGIS API for JS, Cesium]
        J2 --> H2
    end
    
    style A1 fill:#f9f,stroke:#333,stroke-width:2px
    style A2 fill:#f9f,stroke:#333,stroke-width:2px
    style B1 fill:#ccf,stroke:#333,stroke-width:2px
    style B2 fill:#ccf,stroke:#333,stroke-width:2px
    style D1 fill:#cfc,stroke:#333,stroke-width:2px
    style D2 fill:#cfc,stroke:#333,stroke-width:2px
    style E1 fill:#ffc,stroke:#333,stroke-width:2px
    style E2 fill:#ffc,stroke:#333,stroke-width:2px
    style I1 fill:#fcf,stroke:#333,stroke-width:2px
    style I2 fill:#fcf,stroke:#333,stroke-width:2px



OpenLayers和Leaflet主要专注于轻量级的2D地图展示和交互,提供基础的地图操作功能。它们灵活且易于集成,适合快速开发简单的地图应用。相比之下,Mapbox、ArcGIS API for JavaScript和Cesium是更全面的地理信息系统(GIS) 解决方案。这些库不仅提供强大的2D和3D渲染能力,还集成了复杂的空间分析、数据处理和可视化工具。它们能够高效处理和呈现大规模、复杂的地理空间数据,支持更高级的GIS功能和更丰富的可视化效果。选择哪种库取决于项目的具体需求:简单的地图展示可选前者,而需要深度GIS功能和高性能渲染的复杂应用则应考虑后者。

为了避免抽象,本文依然是使用代码为例,并给予详细的注释,以及思想,来对比前后端渲染差异在哪里

  • 前端渲染代码
1️⃣【数据获取环节】- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 该函数通过API请求获取地理数据,使用async/await确保异步操作的顺序。
async function fetchGeoData() {
  const 🌀geoData = await fetch('https://api.example.com/geojson/cities'); // 请求GeoJSON格式的城市数据
  return response.json(); // 返回解析后的JSON数据
}

2️⃣【数据处理环节】- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function processGeoData(🌀geoData) {
  // 筛选出人口超过100,000的城市
  return🌀geoData.features.filter(feature => feature.properties.population > 100000);
}

3️⃣【数据渲染环节】- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 使用地图库Leaflet将GeoJSON数据添加到地图中,L是Leaflet的全局对象
function renderCities(map, 🌀geoData_filter) {
  L.geoJSON(🌀geoData_filter).addTo(map); // 将筛选出的城市数据以GeoJSON格式添加到地图
}

🔼【添加交互】- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//交互一般都是流程之前,因为需要进行交互,才能发送请求处理数据进行渲染这套流程
map.on('click', async (e) => {     // 用户点击地图时获取数据
    const response = await fetchGeoData(); // 请求数据
    const cities = processGeoData(response); // 处理数据
    renderCities(map, cities); // 渲染数据到地图上
});

  • 后端渲染代码
1️⃣【数据获取环节】- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def get_city_data():
    # connection.cursor()创建数据库游标cursor用于执行SQL查询并处理结果
    with connection.cursor() as cursor:
        # ST_AsGeoJSON是PostGIS函数,用于将空间数据转换为GeoJSON格式
        cursor.execute("SELECT name, population, ST_AsGeoJSON(geom) FROM cities")
        # 返回查询结果
        return cursor.fetchall()  

2️⃣【数据处理环节】- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def process_city_data(cities):
    # 初始化一个空列表以存储处理后的数据
    processed_cities = []    
    # 从本地加载训练好的SVM模型 
    svm_model = joblib.load('path/moonless0222/svm_param.pkl')
    for city in cities:
        city_name = city[0] 
        population = city[1]
        # 假设我们要用城市人口作为特征进行车流量预测,创建一个人口特征
        # 实际肯定需要更多特征来提高预测准确性,先将人口数据转换为二维数组
        features = np.array([[population]]) 
        # 使用SVM模型进行车流量预测
        predicted_traffic = svm_model.predict(features)[0] 
        # 创建字典存储这些数据
        processed_cities.append({
            "name": city[0],  # 城市名称
            "population": city[1],  # 城市人口
            "geometry": city[2]  # 城市几何数据(GeoJSON格式)
            "predicted_traffic": predicted_traffic #预测车流量结果
        })
    # 返回处理后的城市数据   
    return processed_cities 

3️⃣【数据渲染环节】- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def render_map(cities, output_file):
    # 此处省略具体的渲染实现
    pass  

4️⃣【数据传输环节】- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def get_map_tile(request, z, x, y):
    # 处理请求并返回瓦片的逻辑,生成瓦片的文件路径
    tile_path = f"tiles/{z}/{x}/{y}.png"  # 
    with open(tile_path, 'rb') as f:
        return HttpResponse(f.read(), content_type="image/png"

从差异来看

  1. 数据获取环节:前端通过HTTP请求(如fetch)从API获取数据。后端通过数据库连接(如Django的ORM或直接数据库查询)从数据库中获取数据。
  2. 数据处理环节:前端通常执行简单的过滤、查询操作。后端可以进行更复杂的处理,如空间分析、聚合计算、机器学习预测等,计算机能力有多大,你就能多大胆。
  3. 数据渲染环节:前端使用JavaScript库(如Leaflet、OpenLayers、Mapbox GL JS)直接在浏览器中渲染地图或者特效。后端使用更重量级的工具(如GDAL、Mapnik、GeoServer)生成的是地图图像或瓦片,并将其发送到前端。
  4. 交互逻辑设计:后端不处理用户交互,所有交互由前端发起,后端只负责响应请求。

涉及到数据传输,就涉及到数据格式,之前我们都知道JSON,对于WebGIS来说,我们还有自己的数据格式:GeoJSON,就像空间数据库对普通数据库的升级,GeoJSON也是JSON的空间升级版。以下是关于一个建筑数据的示例,展示JSON和GeoJSON的区别。

----JSON--------------------------
{
  "name": "Library Building",
  "location": {
    "latitude": 40.785091,
    "longitude": -73.968285
  },
  "height": 30,
  "area": 5000
}

----GEOJSON--------------------------

{
  "type": "Feature",
  "properties": {
    "name": "Library Building",
    "height": 30,
    "area": 5000
  },
  "geometry": {
    "type": "Point",
    "coordinates": [-73.968285, 40.785091]
  }
}

我们从数据结构上分析吧:JSON示例中,地理位置通过一个嵌套对象的形式表达,包含了latitudelongitude,这种方式使得位置的表示较为直接,但并不符合空间数据的标准化表达。相比之下,GeoJSON将数据结构明确划分为propertiesgeometry两个部分,其中geometry专门用于描述地理特征,包括其类型和坐标。这种结构化的设计便于处理和分析空间数据,同时为地理信息提供了更强的语义意义。通过定义Featuregeometry等类型,GeoJSON使得空间数据的处理与解析变得更加简单,并且更符合GIS领域的标准,而且方便可视化,GeoJSON与许多流行的GIS库和工具(如Leaflet、OpenLayers等)兼容,极大地方便了地图应用的开发。

GeoJSON 主要用于传输矢量数据,而不是栅格数据。它设计用来传输地理要素的几何形状和属性,例如点、线、多边形等/由于栅格数据集通常非常大,逐像素传输对于Web应用来说不切实际。瓦片化是将栅格数据转换为WebGIS可用格式的一种有效方法。此外,还有一些专用的栅格数据格式,如GeoTIFF,它可以在需要时用于存储和传输整个栅格数据集,但在WebGIS中通常不会直接使用,而是转换为瓦片格式。栅格数据以瓦片形式传输的原因包括:浏览器一次只需要加载和渲染用户视图范围内的瓦片,而不是整个栅格数据集,这大大提高了性能。瓦片金字塔结构允许栅格数据在不同缩放级别下以不同分辨率提供,适应不同设备和网络条件。瓦片可以被Web服务器或浏览器缓存,这样在用户浏览地图时可以快速加载已访问过的区域(只有在用户请求了一个从未被请求过或未被预先生成的瓦片时,服务器才会执行较为复杂的操作,即调用Web服务代码来生成这个新的瓦片。一旦这个瓦片被创建,它也会被保存下来,以便未来相同的请求可以直接通过文件读取来快速响应,也就是说根本不涉及动态请求)。

数据格式Web GIS适用性原因本地GIS适用性原因
GeoJSON非常适合轻量级、基于JSON,易于Web解析和处理,直接支持webGIS库。适中支持性广泛,但可能不包含所有高级功能或大型数据集的高效处理能力。
Shapefile (.shp)不适合多文件组成,不便于Web传输和解析,通常需要转换为Web兼容格式(如GeoJSON)。非常适合完整的数据包,包括几何、属性、索引,支持复杂数据处理和分析。
KML合适XML结构,支持样式信息,适用于Google Maps/Earth集成,但文件大小和解析效率可能受限。合适在本地GIS软件中有一定支持,但可能不如Shapefile或GeoPackage功能全面。
GeoTIFF (.tif)不适合文件体积大,不适合快速Web传输,主要用于生成瓦片缓存的源数据。非常适合高分辨率,支持多种压缩,适用于存储和处理大尺寸遥感图像或地图切片。
PNG/JPEG适中至高适用于Web地图瓦片传输,简单快速,但不支持地理数据属性。较低主要用于地图输出和展示,而非地理数据的主要存储格式。
MBTiles非常适合专为Web地图设计,支持瓦片数据的高效打包和快速访问。较低主要用于瓦片数据的分发和管理,而非原始地理数据的存储和处理。

从理论上讲,CSV文件用来存储地理空间数据,包括经纬度坐标、属性和位置信息,甚至可以表示简单的拓扑关系。如果你自己编写或者使用现有的Web API来解析CSV文件中的地理数据,你完全可以实现与GeoJSON相似的功能,但是GeoJSON相比,CSV文件需要更多的解析工作,这可能会影响Web应用程序的性能,尤其是在处理大量数据时。但是需要知道,CSV文件通常需要额外的元数据来解释数据的含义,而GeoJSON则内嵌了这种信息。

上述讲完了传输矢量数据的GeoJSON格式,这种格式一般在WFS服务中用于用户在浏览器直接对数据使用框架直接绘制,那么栅格数据呢?常见的栅格数据格式如GeoTIFFECWJPEG2000等都采用了各种压缩技术。GeoTIFF可以支持多种压缩算法,包括RLE和JPEG等。 JPEG2000是一种有损压缩,但也可以配置为无损,ECWMrSID是专门用于压缩大幅面影像的格式,适合存储和传输卫星影像。

  • 为什么不使用GeoJSON来存储栅格???如果要用GeoJSON的方式来存储这张栅格图像,就需要为每个像素记录它的位置和数值,这意味着你需要和像元相同数量的条记录,每一条记录包含像素位置和像素值,没必要。因此,我们用GeoTIFF这种更高效的栅格专用格式(包含起点坐标、像素大小、像素值、投影等)。思路如下:栅格数据通常会有一个“起点”,就像在地图上标明左上角的位置。然后再给出“每个小方格的大小”(像素分辨率),比如“每个方格是10米x10米”,这样就不需要存储每一个像素的绝对地理坐标,因为我们知道整个网格从左上角开始,并且每个像素紧接在上一个之后,空间位置都能推导出来,然后在这基础上用压缩算法来减少数据的大小。

  • 实际应用中的压缩算法往往是混合使用:

  1. 先用四叉树分块:作为一种基于空间分割的压缩方法,适用于有大块相似区域的栅格数据。它通过递归地将图像分割成四个相等的子块,直到每个子块都是均匀的为止。例如,将整个栅格划分为四个区域。如果某个区域的所有像素值相同,则用一个单一节点表示这个区域;如果不相同,则继续将其划分为四个更小的区域,不断地增加树的深度,所以每个节点包含的信息量与其内部的均匀程度有关。
  2. 再对每个块用游程编码:游程长度编码(Run-Length Encoding, RLE)是一种非常简单的无损压缩算法,特别适用于有大量连续相同值的数据。它通过将连续相同的数值记录为一个单一的值及其重复次数来实现压缩。比如栅格数据的一行像素值是1111222233334444,RLE编码将其表示为(1,4), (2,4), (3,4), (4,4),即记录数值和重复的次数。 在地理栅格数据中,比如土地覆盖数据、森林、水域可能会有很多相同的值,R但对于高度变化频繁的栅格比如城市,那么效果则相对较差。
  3. 最后用莫顿码组织存储顺序:莫顿码(Morton Code)也称为Z曲线,是一种用于将二维空间数据一维线性化的编码方式。它把二维或更高维度的栅格数据映射到一个一维的序列中,同时尽量保持数据之间的邻近关系。通过交替编码行和列的坐标来生成一个一维序列,使得相邻的像素在映射后的空间中也尽量保持接近。通过这种方式,栅格数据可以在存储或处理时利用空间局部性。这种方法对于优化访问栅格数据中的邻近像素特别有效,特别是在需要快速地找到某个区域附近的数据时。莫顿码经常被用于索引大规模栅格数据,比如在数据库中存储和查询栅格块,目的是通过这种编码方式更高效地访问栅格的局部数据块。

第五章-WebGIS架构分类与技术栈对比

改进后的 WebGIS 架构分类与技术栈

为确保精确区分并清晰表达,以下是正确整理的 WebGIS 架构分类与技术堆栈。GIS服务器GIS服务的概念将被清晰地区分,避免混淆。


架构类型前端技术后端/服务器技术GIS服务器数据库与其他组件
1. 纯前端应用 + GIS服务器- Vue.js
- React
- OpenLayers
- Leaflet
- 无后端- ArcGIS Server
- GeoServer
- Nginx(静态内容托管)
2. 前后端分离应用 + 应用服务器 + GIS服务器- Angular
- Mapbox GL JS
- Django (Python)
- Express.js (Node.js)
- Spring Boot (Java)
- ArcGIS Server
- MapServer
- PostgreSQL/PostGIS
- Redis (缓存)
3. 中间件应用 + GIS服务器- React
- CesiumJS
- Leaflet
- Koa.js
- Express.js (Node.js)
- ArcGIS Server
- GeoServer
- RabbitMQ(消息队列)
- Elasticsearch(搜索)
4. 微服务架构 + GIS服务器- React
- Cesium
- Spring Cloud (Java)
- NestJS (Node.js)
- ArcGIS Enterprise
- Boundless Server
- Docker(容器化)
- Kubernetes(编排)
- Consul(服务发现)
5. 无服务器架构 (Serverless) + GIS服务器- Svelte
- CesiumJS
- AWS Lambda
- Azure Functions
- Google Cloud Functions
- ESRI ArcGIS Online
- Carto
- API Gateway
- CloudFront(CDN)

各架构类型的详细阐述

1. 纯前端应用 + GIS服务器

在此架构中,应用完全运行于前端,直接与 GIS 服务器进行通信。前端使用 Vue.js 或 React 等框架,并通过 OpenLayers 或 Leaflet 处理 GIS 功能。GIS 服务器如 ArcGIS Server 或 GeoServer 负责提供地图数据和处理请求。

  • GIS服务器:提供地图和地理数据服务。
  • 优点:架构简单,适合小型应用,无需后端服务器,静态页面通过 Nginx 等托管。
  • 应用场景:适用于地理数据展示为主、后端无复杂业务逻辑的轻量级 WebGIS 项目。
2. 前后端分离应用 + 应用服务器 + GIS服务器

该架构采用前后端分离模式,前端使用 Angular 或 Mapbox GL JS,后端使用 Django、Express.js 或 Spring Boot 处理业务逻辑。后端通过 API 与 GIS 服务器(如 ArcGIS Server 或 MapServer)交互,空间数据存储在 PostgreSQL/PostGISRedis 用于加速缓存。

  • GIS服务器:处理地图渲染、空间分析等复杂 GIS 请求。
  • 优点:前后端分离,易于开发和维护;后端扩展性强,适合复杂的业务逻辑处理。
  • 应用场景:适合中型应用,具备用户管理、权限控制和复杂查询等功能。
3. 中间件应用 + GIS服务器

此架构引入了中间件层,通常由 Node.js 的 Koa.js 或 Express.js 处理前后端之间的通信。前端使用 React、CesiumJS 或 Leaflet,GIS服务器依旧负责核心地理数据处理。中间件通过 RabbitMQ 进行异步任务处理,Elasticsearch 用于全文搜索和高效数据检索。

  • GIS服务器:主要用于提供空间数据处理和地图服务。
  • 优点:适用于高并发、大规模数据处理场景,支持异步任务和快速检索。
  • 应用场景:适用于大数据量、高频查询或异步任务处理的 GIS 应用,如交通监控、物流跟踪等。
4. 微服务架构 + GIS服务器

微服务架构将 GIS 功能模块化,拆分为多个独立的微服务,例如地图渲染、空间分析和数据管理服务。通过 Spring CloudNestJS 来管理这些微服务的通信。每个微服务都通过 Docker 进行容器化,通过 Kubernetes 进行编排和管理。GIS服务器则负责提供集中化的地理数据处理服务。

  • GIS服务器:如 ArcGIS Enterprise 或 Boundless Server 提供核心 GIS 服务。
  • 优点:高度灵活和可扩展,适用于复杂的企业级 WebGIS 系统。
  • 应用场景:大型企业级 GIS 系统,支持高并发和动态扩展的应用需求。
5. 无服务器架构 (Serverless) + GIS服务器

此架构通过云平台的 Serverless 计算服务(如 AWS Lambda、Azure Functions、Google Cloud Functions)实现后端逻辑的按需执行,无需持续运行的服务器。前端使用 Svelte 或 CesiumJS,GIS 服务由 ESRI ArcGIS OnlineCarto 提供。API Gateway 负责 API 管理,CloudFront 作为 CDN 提供全球分发。

  • GIS服务:依托云平台的 GIS 服务,如 ArcGIS Online 或 Carto。
  • 优点:无需维护服务器,按需扩展,极大降低了运维成本。
  • 应用场景:适合负载变化大、需求快速迭代的 GIS 应用,如按需生成报告或大规模分布式地图分析。

小结

  • GIS服务器:在所有架构中都扮演着关键角色,它提供了地图渲染、地理数据处理和空间分析服务,是 WebGIS 系统的核心后台支持。
  • GIS服务:可以理解为 GIS 服务器所提供的具体功能接口,如地图服务、空间分析服务等。在无服务器架构下,GIS服务通常由云平台托管和提供。
ESri的企业级方案
flowchart LR
    %% 主体节点
    User([用户]) --> Browser[浏览器]
    
    subgraph Frontend[前端层]
        Browser --> |1.加载| JSAPI[ArcGIS JS API]
        JSAPI --> |2.初始化| MapView[地图视图]
    end
    
    subgraph Service[服务层]
        MapView --> |3.请求| Portal[Portal for ArcGIS]
        Portal --> |4.认证后| AGServer[ArcGIS Server]
        AGServer --> |5.处理| Services[地图服务]
    end
    
    subgraph Data[数据层]
        Services --> |6.查询| ArcSDE[ArcSDE]
        ArcSDE --> |7.读取| DB[(地理数据库)]
    end
    
    %% 发布流程(独立显示)
    subgraph Publish[发布流程]
        Pro[ArcGIS Pro] --> |发布| AGServer
    end
    
    %% 返回路径(保持同层级返回)
    DB --> |8.返回数据| ArcSDE
    ArcSDE --> |9.返回结果| Services
    Services --> |10.响应| MapView
    MapView --> |11.渲染| Browser
    Browser --> |12.显示| User

    %% 样式定义
    classDef frontend fill:#f9f0ff,stroke:#333,stroke-width:2px;
    classDef service fill:#e6f3ff,stroke:#333,stroke-width:2px;
    classDef data fill:#e6ffe6,stroke:#333,stroke-width:2px;
    classDef publish fill:#fff0e6,stroke:#333,stroke-width:2px;
    
    %% 应用样式
    class Browser,JSAPI,MapView frontend;
    class Portal,AGServer,Services service;
    class ArcSDE,DB data;
    class Pro publish;

关于ArcGIS ProArcGIS Server的存在原因和它们各自的角色定位

ArcGIS Pro 和 ArcGIS Server 在GIS系统彼此互补。ArcGIS Pro 是为提供了图形化、直观的工作环境, ArcGIS Server 也能分析,但并不提供用户界面的可视化编辑。空间数据在正式投入生产或发布之前需要大量预处理工作,数据清理、格式转换、投影变换、几何修复、拓扑检查等。这些操作可以通过脚本或服务器自动化处理,但桌面端的可视化操作使得数据分析师在处理复杂空间数据时更加直观和高效。 ArcGIS Pro 极其善于制图出图,能够帮助用户在桌面端创建地图、进行符号化、设置图层样式和执行空间分析模型,并将处理好的数据或地图发布到 ArcGIS Server 上。而ArcGIS Server 作为后台服务平台,专注于发布GIS服务,而不是用于可视化编辑或制图、通常在企业级GIS工作流中会有两类团队: 数据管理团队:使用桌面端GIS工具(如 ArcGIS Pro)进行数据准备和处理,负责数据收集、清理、质量检查和格式转换等工作。 服务器管理团队:使用 ArcGIS Server 管理GIS服务,处理用户请求,执行分析任务,并确保服务的稳定运行。

下表列出了Esri开发一套完整GIS平台的所有主要组件及功能,并给出了它们的开源替代方案。这些组件涵盖了从前端到后端、数据管理、分析处理等多个方面,帮助你全面了解构建一个GIS系统所需要的主要技术栈。

核心组件

组件类型ESRI产品主要功能开源替代方案
GIS服务器ArcGIS Server Enterprise• 地图服务发布
• 空间分析服务
• 地理处理服务
• 空间数据服务
• GeoServer
• MapServer
空间数据库Enterprise Geodatabase• 空间数据存储
• 空间索引
• 空间查询
• 版本管理
• PostgreSQL + PostGIS
Web APIArcGIS API for JavaScript• 地图渲染
• 空间分析
• 数据可视化
• 用户交互
• OpenLayers
• Leaflet

支撑组件

组件类型ESRI产品主要功能开源替代方案说明
门户平台Portal for ArcGIS• 服务管理
• 用户认证
• 内容共享
• GeoNode
• MapStore
GeoNode提供完整的地理空间内容管理
数据处理ArcGIS Pro 3.x• 数据预处理
• 地图制图
• 服务发布
• QGIS
• GRASS GIS
QGIS功能丰富,界面友好,插件生态完善
缓存服务ArcGIS Image Server• 瓦片缓存
• 影像服务
• 动态切片
• GeoWebCache
• MapCache
与GeoServer无缝集成

扩展组件

组件类型ESRI产品主要功能开源替代方案说明
空间分析Spatial Analyst• 栅格分析
• 空间统计
• 地形分析
• SAGA GIS
• R spatial
可按需集成到GeoServer
三维支持Scene Server• 3D可视化
• 场景服务
• Cesium
• ThreeJS
Cesium是Web三维可视化标准
移动开发Runtime SDK• 移动端地图
• 离线功能
• Mapbox Mobile移动端地图开发框架

构建一个完整的GIS平台就像搭建一个房子。GIS服务器是房子的地基,支撑整个系统。空间数据库是储藏室,存储各种数据。Web API是门窗。没有GIS服务器的话,整个系统将无法发布和处理地图服务,前端用户将无法看到地图或进行任何空间分析。即使有数据和前端接口,用户无法通过网络访问它们,系统功能几乎瘫痪。没有空间数据库的话,数据无法被有效存储或高效检索,系统的分析能力会大大受限。虽然可以把数据存成简单的文件(如Shapefile),但性能和查询效率会显著下降,处理大规模数据时尤其不稳定。Web API允许用户查看地图、执行空间查询、操作数据,并进行可视化。没有它的话用户将无法通过网页与系统进行交互,所有GIS功能都只能通过命令行实现。门户平台用于管理、共享和认证用户访问GIS服务。它类似于系统的“前台”,让用户可以浏览、搜索和分享内容,并确保不同用户的访问权限。没有它用户访问和权限管理将会混乱,特别是在多人使用的企业环境下,没有统一的门户管理可能导致安全问题或者服务难以协调共享。桌面GIS工具用于处理、编辑和分析空间数据。这些工具让地理分析师能够在发布前对数据进行检查、修复和优化。没有它空间数据的处理和制图将非常困难。缓存服务让系统可以更快地响应用户请求,尤其是在处理大规模影像数据时。它通过预先生成的瓦片缓存减少服务器负担。没有它每次用户请求地图时服务器都需要重新生成图像。空间分析提供丰富的空间分析功能,支持栅格数据分析、地形分析、空间统计等。没有它很多复杂的空间问题无法解决,比如土壤侵蚀、洪水模拟等高级分析任务。

Arcgis地图的发布的两种模式

flowchart TB
    subgraph 准备阶段
    A[启动ArcGIS Pro] --> B[打开地图项目]
    B --> D[共享]
    end
    
    subgraph 选择发布方式
    D --> E{选择发布平台}
    end
    
    subgraph ArcGIS Server发布流程
    E -->|Server服务器发布| F[01:配置服务器连接]
    F --> G[02:选择服务类型]
    G --> H[03:配置服务参数]
    H --> I[04:数据验证检查]
    end
    
    subgraph ArcGIS Online发布流程
    E -->|Online在线发布| J[登录ArcGIS账号]
    J --> K[选择共享范围]
    K --> L[填写地图信息]
    end
    
    subgraph 发布完成
    I --> M[发布完成]
    L --> M
    M --> N[访问测试]
    N --> O[服务维护]
    end
    
    style A fill:#e1f5fe
    style E fill:#fff3e0
    style M fill:#e8f5e9
    style O fill:#f3e5f5

ArcGIS Server发布流程(WebGIS开发基本都是这个发布模式,Online发布只适合给大学生学习)

功能特点ArcGIS ServerArcGIS Online
API开发能力- 支持完整的ArcGIS API for JavaScript
- 可直接调用REST服务
- 可进行底层API自定义开发
- 支持各类空间分析API
- 仅支持部分JavaScript API功能
- 主要通过Web API调用
- 仅能使用预设的API功能
- 基础的空间分析功能
第三方集成- 可集成开源GIS组件(OpenLayers等)
- 可对接各类数据库
- 可集成业务系统
- 支持自定义地图服务集成
- 主要集成Esri自身产品
- 有限的第三方插件集成
- 难以对接外部系统
- 仅支持特定格式数据
地图服务定制- 可自定义地图切片方案
- 可自定义符号系统
- 可自定义缓存策略
- 可设计专题地图服务
- 使用预设的地图样式
- 有限的符号定制
- 固定的缓存机制
- 基础的专题图功能
空间分析- 支持复杂的空间查询
- 支持网络分析
- 支持地形分析
- 可开发自定义分析模型
- 基础的空间查询
- 简单的缓冲分析
- 预设的分析工具
- 无法自定义分析功能
数据处理能力- 支持大数据处理
- 实时数据更新
- 复杂的数据转换
- 自定义数据处理流程
- 有数据量限制
- 周期性更新
- 基础数据处理
- 预设处理流程
二次开发空间- 完全开放的二次开发
- 可修改源代码
- 可扩展新功能
- 可自定义界面
- 有限的定制空间
- 不可修改核心功能
- 功能扩展受限
- 界面模板固定
  1. 配置服务器连接: 这一步就像告诉软件"你要把地图发到哪台计算机上"
  • 在ArcGIS Pro中点击"服务器连接",填写GIS服务器地址(比如: xxx.xxx.xxx)
  • 输入用户名密码,测试连接是否成功
  1. 选择地图服务类型:

ArcGIS服务类型对应的OGC服务主要用途
Map ServiceWMS (Web Map Service)地图图像浏览服务
Feature ServiceWFS (Web Feature Service)矢量要素访问和编辑服务
Image ServiceWCS (Web Coverage Service)栅格数据访问服务
Geoprocessing ServiceWPS (Web Processing Service)地理处理服务
Tile ServiceWMTS (Web Map Tile Service)切片地图服务
  1. 配置服务参数:
  • 服务名称、最大/最小显示比例、是否缓存、压缩方式、用户可以对地图做什么操作
  1. 数据验证检查
flowchart TB
    subgraph 基础环境准备
    A[Windows Server安装] --> B[安装Nginx]
    A --> C[安装PostgreSQL+PostGIS]
    B & C --> D[环境检查]
    end
    
    subgraph ArcGIS核心组件
    D --> E[安装Portal for ArcGIS]
    D --> F[安装ArcGIS Server]
    D --> G[安装ArcGIS DataStore]
    E & F & G --> H[配置Nginx反向代理]
    end
    
    subgraph Nginx配置
    H --> I[配置SSL证书]
    H --> J[编写反向代理规则]
    I & J --> K[测试Nginx配置]
    end
    
    subgraph 数据库配置
    K --> L[PostGIS空间扩展]
    L --> M[创建地理数据库]
    M --> N[配置数据库连接]
    end
    
    subgraph 发布测试
    N --> O[准备测试数据]
    O --> P[制作测试地图]
    P --> Q[发布测试服务]
    Q --> R[验证服务访问]
    end
    
    style A fill:#e1f5fe
    style H fill:#fff3e0
    style L fill:#e8f5e9
    style R fill:#f3e5f5