矢量数据管理与优化¶
- 作者
杰夫麦克纳
- 联系
jmckenna在gatewaygeomatics.com
- 原作者
主机地理信息系统
- 最后更新
2022-04-26
根据您的需要选择合适的矢量格式¶
就显示速度而言,MapServer的最佳/最佳矢量数据源为 Shapefile 。
最近为矢量性能设计的一种格式是,在MapServer中的地图绘制速度与shapefile相似 FlatGeobuf ,建议用于云性能。
对于数据库,将显示MapServer PostGIS 层的速度非常快,在MapServer源代码中包含了一些专门用于PostGIS+MapServer绘制速度的自定义技巧。因此,推荐使用PostGIS。
SpatiaLite 建议那些需要可移植格式的人使用,并且与MapServer配合使用非常好。
GeoPackage (“GPKG”)在可移植性方面也很受欢迎,并且与MapServer配合得很好。
以下是一些基本的地图绘制速度结果,以及即将发布的MapServer8.0版本代码(在Windows上运行 MS4W ):
Shapefile 0.011s
FlatGeobuf 0.014s
GeoPackage 0.042s
SpatiaLite 0.045s
PostGIS 0.053s
GeoJSON 0.089s
花点时间查看与您选择的格式对应的GDAL驱动程序页面¶
这是关键的一步,因为MapServer依赖GDAL(实际上是项目的OGR部分,它处理向量)来访问矢量数据。每个驱动程序(OGR格式)都有自己的一组功能和开关。找到您的矢量格式并查看其选项 here 。
通过OGR/GDAL连接到您的数据¶
对于MapServer中的数据管理,这应该始终是您的首要步骤之一。有时桌面GIS程序会以某种方式(如大写)显示一种格式或其属性,但您的数据可能不会显示在MapServer中;检查OGR/GDAL如何读取您的数据文件或数据库表,将帮助您管理数据。这个 通过MapServer的OGR矢量层 文档中有很好的例子(对于 OgrInfo 和其他命令)来连接到您的数据。以下是连接到PostGIS数据库并返回空间表列表的示例:
ogrinfo -ro PG:"host=127.0.0.1 user=postgres password=postgres port=5432 dbname=mydb"
INFO: Open of `PG:host=127.0.0.1 user=postgres password=postgres port=5432 dbname=mydb'
using driver `PostgreSQL' successful.
1: popplace (Point)
2: province (Multi Polygon)
然后通过ogrinfo:获取‘Popplace’表的摘要
ogrinfo -ro PG:"host=127.0.0.1 user=postgres password=postgres port=5433 dbname=mydb" popplace -summary
INFO: Open of `PG:host=127.0.0.1 user=postgres password=postgres port=5433 dbname=mydb'
using driver `PostgreSQL' successful.
Layer name: popplace
Geometry: Point
Feature Count: 497
Extent: (-2303861.750000, -681502.875000) - (2961766.250000, 3798856.750000)
Layer SRS WKT:
PROJCS["NAD83 / Canada Atlas Lambert",
GEOGCS["NAD83",
DATUM["North_American_Datum_1983",
SPHEROID["GRS 1980",6378137,298.257222101,
AUTHORITY["EPSG","7019"]],
TOWGS84[0,0,0,0,0,0,0],
AUTHORITY["EPSG","6269"]],
PRIMEM["Greenwich",0,
AUTHORITY["EPSG","8901"]],
UNIT["degree",0.0174532925199433,
AUTHORITY["EPSG","9122"]],
AUTHORITY["EPSG","4269"]],
PROJECTION["Lambert_Conformal_Conic_2SP"],
PARAMETER["standard_parallel_1",49],
PARAMETER["standard_parallel_2",77],
PARAMETER["latitude_of_origin",49],
PARAMETER["central_meridian",-95],
PARAMETER["false_easting",0],
PARAMETER["false_northing",0],
UNIT["metre",1,
AUTHORITY["EPSG","9001"]],
AXIS["Easting",EAST],
AXIS["Northing",NORTH],
AUTHORITY["EPSG","3978"]]
FID Column = gid
Geometry Column = geom
area: Real (0.0)
perimeter: Real (0.0)
popplace_: Real (0.0)
popplace_i: Real (0.0)
unique_key: String (5.0)
name: String (25.0)
name_e: String (20.0)
name_f: String (20.0)
reg_code: Real (0.0)
nts50: String (7.0)
lat: String (7.0)
long: String (7.0)
sgc_code: Real (0.0)
capital: Real (0.0)
pop_range: Real (0.0)
备注
可以使用从ogrinfo返回的范围值粘贴到 Mapfile 的范围参数中。您还可以在该摘要中注意到 PROJCS/AUTHORITY 行,该行声明此数据当前位于 EPSG:3978 投影。
学习和复习各种OGR实用程序来管理您的矢量¶
OGR命令行实用程序非常有用,在MapServer中显示之前,可以很容易地操作源向量文件或数据库。例如,您可能想要将空间文件导入到现有的PostGIS数据库中,这可以通过轻松完成 ogr2ogr 如下所示(获取一个shapefile,将其导入到现有的PostGIS数据库中,并将新表重命名为‘Roadways’):
ogr2ogr PG:"host=127.0.0.1 user=postgres password=postgres port=5432 dbname=mydb" road.shp -nln roadways
备注
其中,ogr2ogr语法实际上是: Ogr2ogr目标源
您也可以导入空间文件,然后将其重新投影到另一个EPSG投影,如下所示(获取shapefile,将其重新投影到Web墨卡托 EPSG:3857 ,将其导入现有的PostGIS数据库,并将新表重命名为‘Road way s3857’):
ogr2ogr -t_srs EPSG:3857 -s_srs EPSG:3978 PG:"host=127.0.0.1 user=postgres password=postgres port=5432 dbname=mydb" road.shp -nln roadways3857
备注
其中,ogr2ogr语法实际上是: ogr2ogr -t_srs (ouput projection) -s_srs (source projection) destination source
查看所有可用的OGR向量实用程序 here 。
为您的数据编制索引¶
当然,这对于任何矢量层来说都是重要的一步,以便在MapServer中快速显示。您可以启用几种类型的索引来加快使用MapServer显示矢量数据的速度:
平铺索引¶
看见 瓦片索引 有关使用MapServer动态马赛克的更多详细信息。
向数据添加空间索引¶
确保您的几何体具有空间索引。
Shapefile:请参见 希普特里
数据库(和GPKG)见下文
向数据添加属性索引¶
如果要按数据中的特定列进行查询或过滤,则应在矢量数据上设置属性索引。
Shapefile:
ogrinfo -sql "CREATE INDEX ON province USING NAME_E" province.shp
应创建2个扩展名为: .ind和.idm
GeoPackage:
ogrinfo -sql "CREATE INDEX IDXnewindexname ON yourtable (yourcolumn)" file.gpkg
拆分数据¶
如果您发现自己制作了多个层,所有层都使用相同的数据集,但是过滤只使用一些记录,那么您可能会做得更好。如果条件是静态的,一种方法是预拆分数据。
这个 ogr2ogr 实用程序可以从数据源中选择某些功能,并将其保存到新的数据源。因此,可以将数据集拆分为几个较小的数据集,这些数据集已经被有效筛选,并删除filter语句。
如果您使用的是shapefile,则 shp2tile 实用程序是实现这一点的一个很好的命令行工具。
备注
对于Windows用户, MS4W 包括shp2tier实用程序和这里提到的所有实用程序。
在 Mapfile 中处理矢量图层¶
请查看文档中的注释 Mapfile 调整和管理 。您还应该检查您的格式是否有任何特定的MapServer注释 矢量数据 文档。
Shapefile注释¶
使用 希普特里 生成空间索引的步骤 shapefile 。这是快速而简单的(“shptree foo.shp”),并生成一个.qix文件。MapServer将自动检测索引并使用它。
备注
切片索引也可以使用 希普特里 。
MapServer还附带了 SORTHHP 实用工具。这将重新组织形状文件,并根据其中一列中的值对其进行排序。如果您通常是按条件筛选的,并且几乎总是按特定列筛选,那么这可以使流程稍微高效一些。
虽然shapefiles是一种非常快速的数据格式, PostGIS 速度也相当快,特别是如果您很好地使用索引,并且有内存用于缓存。
邮政地理信息系统注释¶
使用PostGIS进行索引¶
对性能的最大提升就是索引。确保在“几何”列上有一个gist索引,并且每个记录也应该有一个索引主键。如果使用shp2pgsql,那么这些语句应该创建必要的索引:
ALTER TABLE table ADD PRIMARY KEY (gid);
CREATE INDEX table_the_geom ON table (the_geom) USING GIST;
PostgreSQL还支持重新组织表中的数据,以便按索引进行物理排序。这使得PostgreSQL能够更高效地读取索引数据。使用 CLUSTER command ,例如
CLUSTER the_geom ON table;
除了地理空间组件之外,还可以在数据库服务器本身上执行许多优化。最简单的就是增加 shared_buffers 在 postgresql.conf 文件,这允许PostgreSQL使用更多的内存进行缓存。值得花时间研究一下 Resource Consumption 部分的PostgreSQL文档。
指定唯一ID列¶
要处理查询,MapServer需要唯一的ID列,作为您的PostgreSQL表的一部分。MapServer将尝试猜测唯一ID列,但这样做的代价很高,因为必须进行多个数据库查询;相反,您应该始终在图层的数据语句中使用 使用唯一 语法,例如:
DATA "geom FROM mydata USING UNIQUE myid USING SRID=3857"
如果您的表没有唯一ID列,则可以添加一个列,如下所示:
ALTER TABLE mytable ADD COLUMN unique_id SERIAL PRIMARY KEY;
警告
在较早的PostgreSQL版本中,一个(脏)技巧是使用现有的 OID 然而,作为唯一ID,从PostgreSQL 12.0版本开始,从PostgreSQL中删除了OID。因此,请始终在层的DATA语句中指定映射文件的实际唯一ID列。
PostGIS的调试速度问题¶
您可能会遇到一种情况,即您的PostGIS表在MapServer中绘制得很慢。以下步骤将帮助您检查问题(使用WFS案例):
参见
始终从获取您的层的绘制时间开始,使用 Sp2IMG 命令:
shp2img -m postgis-wfs.map -o ttt.png -map_debug 3 msDrawMap(): Layer 0 (provinces), 1.587s msDrawMap(): Drawing Label Cache, 0.000s msDrawMap() total time: 1.589s msSaveImage(ttt.png) total time: 0.005s freeLayer(): freeing layer at 010E0EC0. msPostGISLayerIsOpen called. msConnPoolClose(host=127.0.0.1 user=postgres password=postgres port=5432 dbname=gmap,01197840)
要诊断问题是出在您的PostGIS表配置上,还是出在MapServer上,请通过执行以下步骤,在psql.exe命令行中执行由MapServer发送的请求:
添加到 Mapfile 的地图级别:
CONFIG "CPL_DEBUG" "ON" CONFIG "MS_ERRORFILE" "/ms4w/tmp/ms_error.txt" DEBUG 5
添加到 Mapfile 的PostGIS图层中:
DEBUG 5
现在使用WFS客户端(如QGIS)并添加您的WFS PostGIS图层
在记事本++中打开“/ms4w/tmp/ms_error.txt”
搜索“msPostGISLayerWhichShape Query:”
该行应该列出从MapServer发送到PostreSQL实例的长查询,可能如下所示:
[Mon Apr 12 11:27:34 2021].207000 msPostGISLayerWhichShapes query: SELECT "gid"::text,"area"::text,"perimeter"::text,"province_"::text,"province_i"::text,"status"::text,"name"::text,"name_e"::text,"name_f"::text,"reg_code"::text,"poly_featu"::text,"island"::text,"island_e"::text,"island_f"::text,ST_AsBinary(("geom"),'NDR') as geom,"gid"::text FROM province WHERE "geom" && ST_GeomFromText('POLYGON((-5814679.36987815 -1504714.04276694,-5814679.36987815 4439806.52253364,5943763.33635122 4439806.52253364,5943763.33635122 -1504714.04276694,-5814679.36987815 -1504714.04276694))',3978) LIMIT 2 OFFSET 0
备注
对于一个QGIS操作,可能会有多个查询发送到MapServer,因此还要在该错误日志中查找其他“msPostGISLayerWhichShape Query:”实例。
现在通过psql.exe连接到该数据库
psql -U postgres -p 5432 -d mydb
使用该错误文件行,从“SELECT”中获取所有内容,并在数据库提示符中,使用“EXPLAIN ANALYE”启动命令,然后粘贴完整的查询,例如:
mydb=# EXPLAIN ANALYZE SELECT "gid"::text,"area"::text,"perimeter"::text,"province_"::text,"province_i"::text,"status"::text,"name"::text,"name_e"::text,"name_f"::text,"reg_code"::text,"poly_featu"::text,"island"::text,"island_e"::text,"island_f"::text,ST_AsBinary(("geom"),'NDR') as geom,"gid"::text FROM province WHERE "geom" && ST_GeomFromText('POLYGON((-5814679.36987815 -1504714.04276694,-5814679.36987815 4439806.52253364,5943763.33635122 4439806.52253364,5943763.33635122 -1504714.04276694,-5814679.36987815 -1504714.04276694))',3978) LIMIT 2 OFFSET 0;
响应将告诉您查询花费了多长时间,例如:
Execution time: 0.293 ms
如果在psql命令行执行查询需要很长时间,那么您应该将精力集中在改进PostGIS表的索引/设置上。
通用数据库(PostGIS、Oracle、SpatiaLite、GeoPackage、Microsoft SQL Server、MySQL)¶
启用连接池¶
默认情况下,MapServer为映射文件中的每个数据库驱动的图层打开和关闭新的数据库连接。如果有多个层从同一个数据库中读取数据,这就没有多大意义了。对于某些数据库(如Oracle),建立连接需要足够长的时间,因此可能会变得非常重要。
尝试将此行添加到数据库层:
PROCESSING "CLOSE_CONNECTION=DEFER"
这会导致MapServer在处理完映射文件之前无法关闭每个层的数据库连接,这可能会减少几秒钟的映射生成时间。
在 Mapfile 的图层级别设置范围¶
此外,出于性能考虑,每个具有数据库连接的图层都应在图层级别设置范围(以及 ows_extent 元数据(如果您通过OGC服务提供),例如:
/* my database layer */ LAYER NAME "provinces" METADATA "wms_title" "Land" "wms_extent" "-2340603.75 -719746.0625 3009430.5 3836605.25" #this helps for performance END #metadata TYPE POLYGON STATUS ON CONNECTIONTYPE postgis CONNECTION "host=127.0.0.1 user=postgres password=postgres port=5432 dbname=gmap" DATA "geom FROM province USING unique gid using srid=3978" EXTENT -2340603.75 -719746.0625 3009430.5 3836605.25 #this helps for performance PROJECTION "init=epsg:3978" END # projection ... END # layer
小技巧
PostGIS用户可以使用 ST_Extent() 用于获取表的边框的空间函数,例如::
SELECT ST_Extent(geom) as table_extent FROM province;
table_extent
----------------------------------------------------
BOX(-2340603.75 -719746.0625,3009430.5 3836605.25)
(1 row)
验证表是否具有空间索引¶
PostgreSQL/PostGIS示例¶
通过psql命令行连接后,描述该表并查找“索引”部分,其中提到几何图形,例如:
Indexes:
"province_pkey" PRIMARY KEY, btree (ogc_fid)
"province_wkb_geometry_geom_idx" gist (wkb_geometry)
地理包示例¶
您可以使用 OgrInfo 要验证您的GPKG表的几何是否具有空间索引,这对性能非常重要(请查看 HasSpatialIndex (Integer) = 1 确认空间索引存在):
ogrinfo -sql "SELECT HasSpatialIndex('countries', 'GEOMETRY')" countries.gpkg
Layer name: SELECT
Geometry: Unknown (any)
Feature Count: 1
Layer SRS WKT:
(unknown)
HasSpatialIndex: Integer (0.0)
OGRFeature(SELECT):0
HasSpatialIndex (Integer) = 1
然后使用ogrinfo添加空间索引:
ogrinfo -sql "SELECT CreateSpatialIndex('parcelle_graphique', 'geom')" PARCELLES_GRAPHIQUES.gpkg
对于WFS服务,禁止使用默认/全图范围¶
对于您的数据库连接,MapServer可能(默认情况下)在执行查询时使用数据的全部范围(这可能导致响应缓慢超过30秒)。您可能应该设置以下图层元数据以禁用此功能:
METADATA
"wfs_use_default_extent_for_getfeature" "false"
END
以下是一个完全增强的层示例:
LAYER
NAME "CLUS"
METADATA
"ows_title" "CLUS"
"ows_srs" "EPSG:3857 EPSG:4326 EPSG:2154"
"wfs_getfeature_formatlist" "geojson"
"wfs_geomtype" "MultiPolygon"
"gml_featureid" "fid"
"gml_include_items" "all"
"wfs_extent" "115134 6049690 1242200 7108930" #set to improve performance for all DB layers
"wfs_use_default_extent_for_getfeature" "false" #set to improve performance for all DB layers
"ows_enable_request" "*"
END #metadata
TYPE POLYGON
CONNECTIONTYPE OGR
CONNECTION "C:/ms4w/apps/RPG_2-0_GPKG_LAMB93_FR-2019/PARCELLES_GRAPHIQUES.gpkg"
DATA "parcelle_graphique"
EXTENT 115134 6049690 1242200 7108930 #set to improve performance for all DB layers
PROCESSING "CLOSE_CONNECTION=DEFER" #set to improve performance for all DB layers
STATUS ON
PROJECTION
"init=epsg:2154"
END #proj
COMPOSITE
OPACITY 100
END #composite
CLASS
NAME "CLUS"
STYLE
OUTLINECOLOR 255 0 255
END #style
END #class
END #layer