GDAL库学习笔记-关于空间参考

转自http://wiki.woodpecker.org.cn/moin/lilin/gdal-spatialref

关于空间参考

好啦,现在让我们开始进入地理的世界

一幅地图除了需要画出地物的形状,更重要的是要画出地物的位置。做GIS的头一项就是要做定位。这也就是GIS数据源和普通数据源的区别--它带有空间信息。GDAL和PIL等图像处理库的最大区别也就在此处。

空间信息包括两个部分。一个是坐标系统,还有一个就是栅格坐标和地理坐标之间的转换。

坐标系统是一个很大的概念。包括很多知识点,可以写厚厚的一本书。我们这里也不想多说,毕竟我在这里不是要普及地理知识,我只在这里讲GDAL中涉及的坐标系统概念。因为我自己对坐标系统学习也并不是太深入(我不是测绘专业的),所以,我的一些理解可能是错误的,希望大家能及时指正。 根据GDAL数据模型文档的解释,GDAL数据集类中包括的坐标系统解释是这样的:

GDAL文档的解释

数据集的坐标系统由OpenGIS WKT字符串定义,它包含了:

1、一个总体的坐标系名

2、一个地理图形坐标系统名

3、一个基准面定义

4、一个椭球体的名字。长半轴(semi-major axis)和反扁率(inverse flattening)

5、本初子午线(prime meridian)名和其与格林威治子午线的偏移值

6、投影方法类型(如横轴莫卡托)

7、投影参数列表(如中央经线等)

8、一个单位的名称和其到米和弧度单位的转换参数

9、轴线的名称和顺序

10、在预定义的权威坐标系中的编码(如EPSG)

更多信息请参考OpenGIS WKT坐标系统定义,以及osr教程文档和OGRSpatialReference类的描述文档

在GDAL中,返回坐标系统的函数是GDALDataset::GetProjectionRef()。它返回的坐标系统描述了地理参考坐标,暗含着仿射地理参考转换,这地理参考转换是由GDALDataset::GetGeoTransform()来返回。

由GCPs地理参考坐标描述的坐标系统是由GDALDataset::GetGCPProjection()返回的。

注意,返回的坐标系统字符串“”表示未知的地理参考坐标系统。

仿射地理转换

GDAL数据集有两种模式描述栅格位置(用点/线坐标系)以及地理参考坐标系之间的关系:首要的也是最普遍的是使用仿射转换,另一种则是GCPs(多控制点定位方式)

  • 仿射变换由6个参数构成,它们由GDALDataset::!GetGeoTransform()返回它们把点/线坐标系(我想这里的(点/线)意思是栅格的(行/列)位置)用下面的关系转换到地理参考空间。

Toggle line numbers
 1 Xgeo = GT(0) + Xpixel*GT(1) + Yline*GT(2)
 2 Ygeo = GT(3) + Xpixel*GT(4) + Yline*GT(5)
 3 

假设指北针向上的影像,GT2和GT4参数是0,而GT1是象元宽,GT5是象元高,(GT0,GT3)点位置是影像的左上角。

注意,上面所说的点/线坐标系是从左上角(0,0)点到右下角,也就是坐标轴从左到右增长,从上到下增长的坐标系。点/线位置中心是(0.5,0.5)

GCPs

数据集可以由一系列控制点来定义空间参考坐标系所有的GCPs共用一个地理参考坐标系(由GDALDataset::GetGCPProjection()返回,每个GCP(由GDAL_GCP定义)包含下面内容:

Toggle line numbers
 1  typedef struct
 2 {
 3     char        *pszId;
 4     char        *pszInfo;
 5     double      dfGCPPixel;
 6     double      dfGCPLine;
 7     double      dfGCPX;
 8     double      dfGCPY;
 9     double      dfGCPZ;
 10 } GDAL_GCP;
 11 

pszid是本GCP在数据集中一系列GCP点中惟一的标示字符串,它常常是数字,但不一定总是。有可能在GCP状态中包含机器可分析信息,虽然现在还不行。

(dfGCPPixel,dfGCPLine)位置是栅格中的GCP位置,(dfGCPX,dfGCPY,dfGCPZ)位置是联合的地理参考位置(Z通常是0)

GDAL数据模型没有实现由GCPs...产生坐标系的变化的机制,而是把它留给实际应用。但是1到5阶多项式是通常使用的方法。

通常一个数据集会包含仿射地理变换。和GCPS中的一个或者两个都没有。两个都有很少见。而且无法用权威坐标系定义。

我的理解

上面文档说得很玄,还是让我们看看坐标系统究竟是什么样子。

Toggle line numbers
 1 >>> import gdal
 2 >>> dataset = gdal.Open("e:/gisdata/gtif/spot.tif")
 3 >>> dir(dataset)
 4 ['AddBand','AdviseRead','BuildOverviews','FlushCache','GetDescription','Get
 5 Driver','GetGCPCount','GetGCPProjection','GetGCPs','GetGeoTransform','GetMe
 6 tadata','GetProjection','GetProjectionRef','GetRasterBand','GetSubDatasets',
 7  'RasterCount','RasterXSize','RasterYSize','ReadAsArray','ReadRaster','Refr
 8 eshBandInfo','SetDescription','SetGCPs','SetGeoTransform','SetMetadata','Se
 9 tProjection','WriteRaster','__del__','__doc__','__init__','__module__','_b
 10 and','_o']
 11 >>> dataset.GetProjectionRef()
 12 'PROJCS["unnamed",GEOGCS["NAD27",DATUM["North_American_Datum_1927",SPHEROID["Cla
 13 rke 1866",6378206.4,294.9786982139006,AUTHORITY["EPSG","7008"]],AUTHORITY["EPSG"
 14 ,"6267"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPS
 15 G","4267"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],
 16 PARAMETER["central_meridian",-105],PARAMETER["scale_factor",0.9996],PARAMETER["f
 17 alse_easting",500000],PARAMETER["false_northing",UNIT["metre",1,AUTHORITY["EP
 18 SG","9001"]],"26713"]]'
 19 >>>
 20 

WKT

最后那一大串字符串就代表了坐标系统了,没有缩进编排会看晕了(这个东西我第一次看以为是脚本)。GDAL的坐标系统的标示方法是用OpenGIS的WKT字符串表示。当然也混合了EPSG权威编码,关于WKT可以看这里。 它是绝对的标准,完全解释了坐标系统的组成,结构和层次,它是由一些层次嵌套构成的。

Toggle line numbers
 1 Coordinate System WKT
 2 <coordinate system>  = <horz cs> | <geocentric cs> | <vert cs> | <compd cs> | <fitted cs> | <local cs>
 3 <horz cs>            = <geographic cs> | <projected cs>
 4 <projected cs>       = PROJCS["<name>",<geographic cs>,<projection>,{<parameter>,}* <linear unit> {,<twin axes>}{,<authority>}]
 5 <projection>         = PROJECTION["<name>" {,<authority>}]
 6 <geographic cs>      = GEOGCS["<name>",<datum>,<prime meridian>,<angular unit> {,<twin axes>} {,<authority>}]
 7 <datum>              = DATUM["<name>",<spheroid> {,<to wgs84>} {,<authority>}]
 8 <spheroid>           = SPHEROID["<name>",<semi-major axis>,<inverse flattening> {,<authority>}]
 9 <semi-major axis>    = <number>
 10 <inverse flattening> = <number>
 11 <prime meridian>     = PRIMEM["<name>",<longitude> {,<authority>}]
 12 <longitude>          = <number>
 13 <angular unit>       = <unit>
 14 <linear unit>        = <unit>
 15 <unit>               = UNIT["<name>",<conversion factor> {,<authority>}]
 16 <conversion factor>  = <number>
 17 <geocentric cs>      = GEOCCS["<name>",<linear unit> {,<axis>,<axis>} {,<authority>}]
 18 <authority>          = AUTHORITY["<name>","<code>"]
 19 <vert cs>            = VERT_CS["<name>",<vert datum>,<linear unit>,{<axis>,} {,<authority>}]
 20 <vert datum>         = VERT_DATUM["<name>",<datum type> {,<authority>}]
 21 <datum type>         = <number>
 22 <compd cs>           = COMPD_CS["<name>",<head cs>,<tail cs> {,<authority>}]
 23 <head cs>            = <coordinate system>
 24 <tail cs>            = <coordinate system>
 25 <twin axes>          = <axis>,<axis>
 26 <axis>               = AXIS["<name>",NORTH | SOUTH | EAST | WEST | UP | DOWN | OTHER]
 27 <to wgs84s>          = TOWGS84[<seven param>]
 28 <seven param>        = <dx>,<dy>,<dz>,<ex>,<ey>,<ez>,<ppm>
 29 <dx>                 = <number>
 30 <dy>                 = <number>
 31 <dz>                 = <number>
 32 <ex>                 = <number>
 33 <ey>                 = <number>
 34 <ez>                 = <number>
 35 <ppm>                = <number>
 36 <fitted cs>          = FITTED_CS["<name>",<to base>,<base cs>]
 37 <to base>            = <math transform>
 38 <base cs>            = <coordinate system>
 39 <local cs>           = LOCAL_CS["<name>",<local datum>,<unit>,{,<axis>}* {,<authority>}]
 40 <local datum>        = LOCAL_DATUM["<name>",<authority>}]
 41 

这个结构的阅读方法是这样的,所有的尖括号<>中的值都是一个需要被替代的东西(我觉得应该叫做一个类型)。你可以在表的其余中找到对应的类型描述,但是这个类型是不能直接写出来的,也就是说相当于一个链接,也可以看成是一个指针或一个引用,它指向一个对象,但是却不是一个对象,你需要用一个确实的东西来代替它。而且文档的下面有各种类型的解释。在花括号{}中的是可选的项目,表现在实际的坐标表示中,就是可写可不写。等号=是用来表示类型的表达形式。 用 | 分割开的是并列的选项,也就是在实际坐标表示中你只能选用其中的一个。*号表示有数个,多个相同的类型组成。除了上面的其他的符号就代表确实的东西([]是需要实际写入坐标系表示字符串的一部分,不是这个语法表达表中的表达符号,冒号" 逗号,同样也是实体的一部分),是需要到坐标系中的。最后注意书写顺序从<coordinate system>一条条对应下来,就可以了。比如从<coordinate system>开始,它不是一个东西,而它是<horz cs>等类型的其中一个类型。如果我们判断我们需要的坐标系是一个<horz cs>类型,我们就去<horz cs>中找,<horz cs>是<projected cs>等类型其中一个类型。如果我们判断我们需要的坐标系是一个投影坐标类型,我们就到<projected cs>中找,<projected cs>的内容不再是虚的了,我们就把PROJ[..,..,..]结构写下来。PROJ["<name>",<geographic cs>,<projection>,{<parameter>,}* <linear unit> {,<twin axes>}{,<authority>}],其中包括了<name>类型,<geographic cs>类型,<projection>类型,数个可选的<parameter>类型,<linear unit>类型,还有可选的<twin axes>和<authority>类型组成的。然后到每个类型中去找对应的结构,直到全部内容都写完为止。需要解释的是<name> <code>是对应的实际的类型的名称和编码。不是在表中有对应。就如上面的例子,基准面名是"North_American_Datum_1927",对应的<datum>类型的<name>类型就用该名称代替。也就是说各种坐标、投影、单位、编码,数值也都算做一个单独的类型。最后直到把那些类型都用实际的东西填满为止。

解释起来很麻烦(汗都说出来了),如果你还是不理解,算了,就当我没说,其实你可以完全不管这些的(但是如果你要写,可以看看这里这里来获取更多有关于坐标系统和其参数的信息,你还可以下载postgresql,如果安装了postgis插件,你就拥有了几乎所有常用的坐标系统的wkt表示)。如果我们不亲自写不同坐标间的转换函数。我们只要关心两个坐标系统是否完全一样,其他都可以不去管它。要是说起不同坐标系统之间转换,又有一大堆的东西要说了,还是以后开个专门的页面写写吧。对于我们来说重要的是另外一个部分--坐标转换。

坐标转换

Toggle line numbers
 1 >>> dataset.GetGCPs()
 2 []
 3 >>> dataset.GetGeoTransform()
 4 (590000.0,20.0,0.0,4928000.0,-20.0)
 5 >>>
 6 

这是我们主要要对付的。只有通过这几个数据,我们才可以知道自己手中这张图该如何铺展到绘制平面上。上面说过了,栅格坐标到地理坐标之间的转换可以通过两种方式来进行:一种是仿射坐标转换,一种是GCPs。一般来说,仿射坐标转换比较常见。仿射坐标转换就是通过几个值来进行栅格框架到地理框架的映射。上面的例子中出现了6个值。这六个值可以用下面的

Toggle line numbers
 1 Xgeo = GT(0) + Xpixel*GT(1) + Yline*GT(2)
 2 Ygeo = GT(3) + Xpixel*GT(4) + Yline*GT(5)
 3 

公式来解释,而且数组顺序对应起来了,理解起来也不困难。至于几个参数究竟是什么意思,这里讲得更清楚:

Toggle line numbers
 1 adfGeoTransform[0] /* top left x */
 2 adfGeoTransform[1] /* w-e pixel resolution */
 3 adfGeoTransform[2] /* rotation,0 if image is "north up" */
 4 adfGeoTransform[3] /* top left y */
 5 adfGeoTransform[4] /* rotation,0 if image is "north up" */
 6 adfGeoTransform[5] /* n-s pixel resolution */
 7 

看起来和ESRI为GeoTiff定义的tfw文件格式很像。至于是否真的是完全对应,我还不敢说。

有了上面的公式,我们就可以把栅格的每个点代入公式来求出其在地球上的实际位置(当然,求出的数值的单位是在坐标系统中定义的那个,而非经纬度坐标,要求经纬度坐标,还要通过一步转换)。当然我们可以不用这么麻烦,只要求出整个图像外框上下左右四个边界就可以定下整个图像的位置,因为图像都是矩形的(那些非矩形的图像其实是有另外一层二值的掩膜图像(mask)叠加而成的,这种图像只有两种值,一种是0,一种是1,1代表按照图像原色显示,0代表不显示)。图像中某点的地理位置可以通过外框四点线性计算。

现在看看我们这张图的边界地理范围:

Toggle line numbers
 1 >>> dataset.GetGeoTransform()
 2 (590000.0,-20.0)
 3 >>> dataset.RasterXSize
 4 950
 5 >>> dataset.RasterYSize
 6 700
 7 >>>
 8 

上边框和左边框都不用算了,是4928000和590000。 求右下角的坐标,代入公式:

右边框 = Xgeo = GT(0) + Xpixel*GT(1) + Yline*GT(2) = 590000.0 + 950*20.0 + 700*0.0 = 609000.0

下边框 = Ygeo = GT(3) + Xpixel*GT(4) + Yline*GT(5) = 4928000.0 + 950*0.0 + 700*(-20.0) = 4914000.0

开ArcGIS验证一下,没错。

Toggle line numbers
 1 In projected or local coordinates
 2 Left: 590000.000000
 3 Right: 609000.000000
 4 Top: 4928000.000000
 5 Bottom: 4914000.000000
 6 

好了,有了这些,就可以把图像和地理坐标联系起来了。有了地理坐标,就可以把图像在照屏幕上预先定好的坐标系画出来了。

需要注意的一点是,对于GeoTiff数据源来说,坐标系统和仿射变换参数有两种存储方式,一种是直接存储在.tif文件内部,这种存储方式是按照tif内部的的键值对方式来存储的。具体的一些细节可以参看这里。官方的说明pdf可以在这里下载。也可以把图像仅仅存储到tif文件中,而把坐标系统和仿射参数什么的单独提取出来,创建两个文件,一个是prj文件,存放WKT坐标系字符串,一个是tfw文件,存放仿射转换参数。

小插曲:

关于tfw文件格式,网络流传几种说法。一种是这样的:

特别是这几行:

行 说明

1 地图单元中的一个象素在X方向上的X分辨率尺度。

2 平移量。

3 旋转量。

4 地图单元中的一个象素在Y方向上的Y分辨率尺度的负值。

5 象素1,1(左上方)的X地坐标。

6 象素1,1(左上方)的Y地坐标。

这种说法的源头可能是这个,但是他的参数个数是8个 第二种则是在gdal文档中的如下的解释

Toggle line numbers
 1 WLD -- ESRI World File
 2 A world file file is a plain ASCII text file consisting of six values separated by newlines. The format is:
 3  pixel X size
 4  rotation about the Y axis (usually 0.0)
 5  rotation about the X axis (usually 0.0)
 6  negative pixel Y size
 7  X coordinate of upper left pixel center
 8  Y coordinate of upper left pixel center
 9 For example:
 10 60.0000000000
 11 0.0000000000
 12 0.0000000000
 13 -60.0000000000
 14 440750.0000000000
 15 3751290.0000000000
 16 You can construct that file simply by using your favorite text editor.
 17 World file usually has suffix .wld,but sometimes it may has .tfw,tifw,.jgw or other suffixes depending on the image file it comes with.
 18 

可以看出参数2和3和第一种的解释不一样。我现在宁可相信GDAL做的第二种解释(如果不是,那著名的grass不是白搞了?),但是这种说法也不一定,因为在gdal中直接解释的是wld文件,别名才是tfw,有没可能tfw有其他官方定义而非ESRI为惟一标准呢?如果去查google,中文和英文的解释会有很多种,越看只能越混乱。幸好大多数情况下是0 -_-! 不管了,先凑合这么用吧。如果谁知道ESRI官方解释,不妨贴上来大家共享)

GCPs

至于GCPs,那就麻烦了。不过玩过arcview或者mapinfo的配准的应该理解起来都没什么问题。要配准一张栅格图(把一个没有空间信息的图片变成有空间信息的栅格数据集),只要在一个已知的坐标系统中定位几个控制点,然后输入这几个点的地理位置。软件通过读取这几个点在图像中的位置,以及输入的这几个点对应的地理位置,建立一个拟合方程,用方程来描述其他点在这个坐标系统中的地理位置。当然,在不同投影不同坐标系不同软件中拟合方程处理可能不同。所以拟合出来的坐标可能有一些差异。这个过程不好试验也不好验证,手上也没有相关数据,所以在这里就不多说了。

反馈

如果您发现我写的东西中有问题,或者您对我写的东西有意见,请登陆这个论坛

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读601次。Oracle的数据导入导出是一项基本的技能,但是对于懂数据库却不熟悉Oracle的同学可能会有一定的障碍。正好在最近的一个项目中碰到了这样一个任务,于是研究了一下Oracle的数据导入导出,在这里跟大家分享一下。......_oracle 迁移方法 对比
文章浏览阅读553次。开头还是介绍一下群,如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。加群请联系 liuaustin3 ,在新加的朋友会分到2群(共700多人左右 1 + 2)。最近我们在使用MYSQL 8 的情况下(8.025)在数据库运行中出现一个问题 参数prefer_order_i..._mysql prefer_ordering_index
文章浏览阅读3.5k次,点赞3次,收藏7次。折腾了两个小时多才成功连上,在这分享一下我的经验,也仅仅是经验分享,有不足的地方欢迎大家在评论区补充交流。_navicat连接opengauss
文章浏览阅读2.7k次。JSON 代表 JavaScript Object Notation。它是一种开放标准格式,将数据组织成中详述的键/值对和数组。_postgresql json
文章浏览阅读2.9k次,点赞2次,收藏6次。navicat 连接postgresql 注:navicat老版本可能报错。1.在springboot中引入我们需要的依赖以及相应版本。用代码生成器生成代码后,即可进行增删改查(略)安装好postgresql 略。更改配置信息(注释中有)_mybatisplus postgresql
文章浏览阅读1.4k次。postgre进阶sql,包含分组排序、JSON解析、修改、删除、更新、强制踢出数据库所有使用用户、连表更新与删除、获取今年第一天、获取近12个月的年月、锁表处理、系统表使用(查询所有表和字段及注释、查询表占用空间)、指定数据库查找模式search_path、postgre备份及还原_pgsql分组取每组第一条
文章浏览阅读3.3k次。上一篇我们学习了日志清理,日志清理虽然解决了日志膨胀的问题,但就无法再恢复检查点之前的一致性状态。因此,我们还需要日志归档,pg的日志归档原理和Oracle类似,不过归档命令需要自己配置。以下代码在postmaster.c除了开启归档外,还需要保证wal_level不能是MINIMAL状态(因为该状态下有些操作不会记录日志)。在db启动时,会同时检查archive_mode和wal_level。以下代码也在postmaster.c(PostmasterMain函数)。......_postgresql archive_mode
文章浏览阅读3k次。系统:ubuntu22.04.3目的:利用向日葵实现windows远程控制ubuntu。_csdn局域网桌面控制ubuntu
文章浏览阅读1.6k次。表分区是解决一些因单表过大引用的性能问题的方式,比如某张表过大就会造成查询变慢,可能分区是一种解决方案。一般建议当单表大小超过内存就可以考虑表分区了。1,继承式分区,分为触发器(trigger)和规则(rule)两种方式触发器的方式1)创建表CREATE TABLE "public"."track_info_trigger_partition" ( "id" serial, "object_type" int2 NOT NULL DEFAULT 0, "object_name..._pg数据表分区的实现
文章浏览阅读3.3k次。物联网平台开源的有几个,就我晓得的有、、thingskit、JetLink、DG-iot(还有其他开源的,欢迎在评论区留言哦!),然后重点分析了下ThingsBoard、ThingsPanel和JetLink,ThingsBoard和Jetlinks是工程师思维产品,可以更多的通过配置去实现开发的目的,ThingsPanel是业务人员思路产品,或者开发或者用,避免了复杂的配置带来的较高学习门槛。ThingsBoard和Jetlinks是Java技术体系的,ThingsPanel是PHP开发的。_jetlinks和thingsboard
文章浏览阅读3.8k次。PostgreSQL 数据类型转换_pgsql数字转字符串
文章浏览阅读7k次,点赞3次,收藏14次。在做数据统计页面时,总会遇到统计某段时间内,每天、每月、每年的数据视图(柱状图、折线图等)。这些统计数据一眼看过去也简单呀,不就是按照时间周期(天、月、年)对统计数据进行分个组就完了嘛?但是会有一个问题,简单的写个sql对周期分组,获取到的统计数据是缺失的,即没有数据的那天,整条记录也都没有了。如下图需求:以当前月份(2023年2月)为起点,往后倒推一年,查询之前一年里每个月的统计数据。可见图中的数据其实是缺少的,这条sql只查询到了有数据的月份(23年的1月、2月,22年的12月)_如何用一条sql查出按年按月按天的汇总
文章浏览阅读3.8k次,点赞66次,收藏51次。PostgreSQL全球开发小组与2022年10月13日,宣布发布PostgreSQL15,这是世界上最先进的开源数据库的最新版本_mysql8 postgresql15
文章浏览阅读1.3k次。上文介绍了磁盘管理器中VFD的实现原理,本篇将从上层角度讲解磁盘管理器的工作细节。_smgrrelationdata
文章浏览阅读1.1k次。PostgreSQL设置中文语言界面和局域网访问_postgressql汉化
文章浏览阅读4.2k次。PostgreSQL 修改数据存储路径_如何设置postgresql 数据目录
文章浏览阅读4.7k次。在项目中用到了多数据源,在连接postgres数据库时,项目启动报错,说数据库连接错误,说dual不存在,网上好多教程都是说数据库查询的时候的大小写问题,而这个仅仅是连接,咋鞥却处理方法是修改application-dev.yml中的配置文件.项目中的druid参数是这样的:确实在配置文件中有个查询语句。_relation "dual" does not exist
文章浏览阅读4.9k次。PostgreSQL是一款强大的关系型数据库,但在实际使用过程中,许多用户经常会遇到慢SQL的问题。这些问题不仅会降低数据库性能,还会直接影响业务流程和用户体验。因此,本文将会深入分析PostgreSQL慢SQL的原因和优化方案,帮助用户更好地利用这个优秀的数据库系统。无论你是初学者还是专业开发者,本文都将为你提供实用的技巧和方法,让你的PostgreSQL数据库始终保持高效快速。_postgresql数据库优化
文章浏览阅读1.6k次。Linux配置postgresql开机自启_linux 启动pgsql
文章浏览阅读2k次。本篇介绍如何在centos7系统搭建一个postgresql主备集群实现最近的HA(高可用)架构。后续更高级的HA模式都是基于这个最基本的主备搭建。_postgresql主备