0%

QGIS 空间索引编码实现

QGIS 空间索引使用 R 树索引,主要底层实现由库 libspatialindex 提供。

构建空间索引

QGIS 原生算法包含“创建空间索引”算法,其参数仅包含一项 – 输入图层。该算法由 C++ 实现,编码如下:

调用 QgsVectorDataProvider 的 createSpatialIndex() 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
QVariantMap QgsSpatialIndexAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QgsVectorLayer *layer = parameterAsVectorLayer( parameters, QStringLiteral( "INPUT" ), context );

if ( !layer )
throw QgsProcessingException( QObject::tr( "Could not load source layer for %1." ).arg( QLatin1String( "INPUT" ) ) );

QgsVectorDataProvider *provider = layer->dataProvider();

if ( provider->capabilities() & QgsVectorDataProvider::CreateSpatialIndex )
{
if ( !provider->createSpatialIndex() )
{
feedback->pushInfo( QObject::tr( "Could not create spatial index" ) );
}
}
else
{
feedback->pushInfo( QObject::tr( "Layer's data provider does not support spatial indexes" ) );
}

QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), layer->id() );
return outputs;
}

QgsVectorDataProvider 的 createSpatialIndex() 为虚函数,由其派生类实现

bool QgsOgrProvider::createSpatialIndex()

  • QgsOgrProvider 仅支持 shp gpkg sqlite 构建空间索引,对其他数据格式不支持。
  • 是线程不安全的,查询和更新必须从互斥中运行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
bool QgsOgrProvider::createSpatialIndex()
{
QgsCPLHTTPFetchOverrider oCPLHTTPFetcher( mAuthCfg );
QgsSetCPLHTTPFetchOverriderInitiatorClass( oCPLHTTPFetcher, QStringLiteral( "QgsOgrProvider" ) );

if ( !mOgrOrigLayer )
return false;
if ( !doInitialActionsForEdition() )
return false;

QByteArray layerName = mOgrOrigLayer->name();
if ( mGDALDriverName == QLatin1String( "ESRI Shapefile" ) )
{
QByteArray sql = QByteArray( "CREATE SPATIAL INDEX ON " ) + quotedIdentifier( layerName ); // quote the layer name so spaces are handled
QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( QString::fromUtf8( sql ) ), 2 );
mOgrOrigLayer->ExecuteSQLNoReturn( sql );

if ( !mFilePath.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) )
return true;

QFileInfo fi( mFilePath ); // to get the base name
//find out, if the .qix file is there
return QFileInfo::exists( fi.path().append( '/' ).append( fi.completeBaseName() ).append( ".qix" ) );
}
else if ( mGDALDriverName == QLatin1String( "GPKG" ) ||
mGDALDriverName == QLatin1String( "SQLite" ) )
{
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
QMutex *mutex = nullptr;
#else
QRecursiveMutex *mutex = nullptr;
#endif
OGRLayerH layer = mOgrOrigLayer->getHandleAndMutex( mutex );
QByteArray sql = QByteArray( "SELECT CreateSpatialIndex(" + quotedIdentifier( layerName ) + ","
+ quotedIdentifier( OGR_L_GetGeometryColumn( layer ) ) + ") " ); // quote the layer name so spaces are handled
mOgrOrigLayer->ExecuteSQLNoReturn( sql );
return true;
}
return false;
}

bool QgsMemoryProvider::createSpatialIndex()

  • 基于内存的图层
  • 此处调用库 libspatialindex 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool QgsMemoryProvider::createSpatialIndex()
{
if ( !mSpatialIndex )
{
mSpatialIndex = new QgsSpatialIndex();

// add existing features to index
for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
{
mSpatialIndex->addFeature( *it );
}
}
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void QgsSpatialIndexData::initTree( IDataStream *inputStream = nullptr )
{
// for now only memory manager
mStorage = StorageManager::createNewMemoryStorageManager();

// R-Tree parameters
double fillFactor = 0.7;
unsigned long indexCapacity = 10;
unsigned long leafCapacity = 10;
unsigned long dimension = 2;
RTree::RTreeVariant variant = RTree::RV_RSTAR;

// create R-tree
SpatialIndex::id_type indexId;

if ( inputStream && inputStream->hasNext() )
mRTree = RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, *inputStream, *mStorage, fillFactor, indexCapacity,
leafCapacity, dimension, variant, indexId );
else
mRTree = RTree::createNewRTree( *mStorage, fillFactor, indexCapacity,
leafCapacity, dimension, variant, indexId );
}

OSGeo 国内镜像源

Windows

OSGeo4w 镜像:武汉大学地理加权建模实验室,镜像手动同步,有时会造成软件出现 Bug。

Ubuntu

ubuntugis 反向代理:科大镜像

1
sudo sed -i 's/ppa.launchpad.net/mirrors.ustc.edu.cn/g' /etc/apt/sources.list/*

Docker

PostGIS

GitHub 仓库: zy6p/docker-postgis

1
docker pull registry.cn-hangzhou.aliyuncs.com/opengis/postgis

Zoo

1
2
git clone git@github.com:zy6p/zoo-project.git
docker-compose up -d

ZOO-Project

ZOO-Project 是一个 WPS(Web Processing Service)框架,实现了 OGC 的 WPS 1.0.0 和 WPS 2.0.0 标准。

ZOO-Project 是一个用C、Python 和 JavaScript 编写的WPS(网络处理服务)实现。它是一个开源的平台,实现了由开放地理空间协会(OGC)编辑的WPS 1.0.0和WPS 2.0.0标准。

ZOO-Project 为创建和连锁符合WPS的网络服务提供了一个开发者友好的框架。它的主要目标是提供通用的和符合标准的方法来使用现有的开源库和算法作为WPS。它还为创建新的创新网络服务和应用提供了有效的工具。

ZOO-Project 能够在线处理地理空间或非地理空间数据。它的核心处理引擎(又称ZOO-Kernel)让你在可靠的软件和库的基础上执行一些现有的ZOO-服务。它还使您能够从新的或现有的源代码中创建自己的WPS服务,这些代码可以用七种不同的编程语言编写。这让您可以简单地将代码编成或变成WPS服务,具有直接的配置和标准编码方法。

ZOO-Project 在数据输入和输出方面非常灵活,因此您可以处理几乎任何一种存储在本地或从远程服务器和数据库访问的数据。ZOO-Project在数据处理和整合新的或现有的空间数据基础设施方面表现出色,因为它能够与地图服务器通信,并能整合网络绘图客户端。

特点

  1. 模块化。分为4个模块,分别是 ZOO-Kernel,ZOO-Services,ZOO-API,ZOO-Client。
  2. 扩展性。开发人员可以使用任意熟悉的语言开发相关算法,无需像 GeoServer 只能使用 JAVA。

安装

docker 安装

使用作者发布在阿里云上的 docker 镜像部署.

1
2
git clone git@github.com:zy6p/zoo-project.git
docker-compose up -d

接下来可以访问 http://localhost:8166/

GIS 开发指南

笔者能力有限,仅谈谈自己理解的 GIS 开发。

开发经历

先后进行过地理数据处理(Python 开发),客户端软件(C++)、WebGIS(前端)开发,均在 GitHub 开源。

OpenGIS

开源 GIS 简介

开源 GIS 软件允许我们查看源码,自由使用,对学习 GIS 底层逻辑、编程技术大有裨益。

为什么选择开源 GIS

专用的 GIS 软件( ArcGIS ),可以让我们轻松完成所有 GIS 工作。但是,该软件通常非常昂贵,并不能查看源码,不能从中学到编码技能。

GIS 供应商有时会为教育活动提供例外,他们提供了更便宜或免费或容易破解的软件。他们这样做的目的是,如果老师和学习者了解他们的软件,他们将不愿意学习其他软件包。当学习者离开学校时,他们将进入工作场所并购买商业软件,而永远不会知道他们可以使用免费的替代品。

如果以后想进入互联网企业,掌握开源 GIS 开发技术比掌握 ArcGIS 竞争力大多了。

GIS 教育现状

ArcGIS 几乎等于 GIS

  • 大二有一门课是地理信息系统导论,课程所用软件就是 ArcGIS
  • 大三的空间分析也是用的 ArcGIS
  • 大三下的地理信息系统实践还是用的 ArcGIS

关于 ArcGIS

合理怀疑 ArcGIS 破解这么容易就是 ESRI 公司故意的。目的就是让大家熟悉 ArcGIS 产品,然后卖 ArcGIS Pro 。无论百度、谷歌还是淘宝,几乎没有可用的 Pro 的破解版。

主流开源 GIS 软件

桌面 GIS

  • QGIS: 桌面 GIS 软件大哥,能实现可视化地理数据、空间分析等功能,界面美观易用,支持插件扩展,提供 python api 以供开发。
  • GRASS: 拥有众多空间分析功能,在 OSGEO 组织推动下绝大部分功能集成进 QGIS 了。
  • SAGA: 空间分析软件,也被集成到 QGIS 了。

GIS 库(C++)

  • GDAL: 抽象地理数据库,GIS 的底层核心,影响巨大。
  • PROJ: 投影库,被 GDAL 使用。
  • GEOS: 二维矢量库,被 PostGIS 使用,提供有许多算法。

GIS 服务器

  • GeoServer: 主流 GIS 服务器,支持 WMS WFS WCS WTMS 等。

WebGIS 客户端

  • OpenLaysers: 功能全面的 JavaScript 库,支持多种投影,支持多种 OGC 服务。
  • Leaflet: 轻量级 JS 库,可以使用插件扩展。
  • Cesium: 三维 JS 库,确立了三维规范。

更多

更多 GIS 相关开源项目可以关注如下资源:

  • OSGEO: 开源 GIS 组织。
  • awesome-gis: 有趣的 GIS 项目集。
  • OGC: 制定了众多 GIS 规范。

基础知识

GIS 基础概念

传统

  • 几何: 点、线、面、集合
  • 数据: 矢量、栅格、Tin
  • 格式: Shp、GeoJSON、GeoPackage、GeoTIFF
  • 地理: 经纬度、坐标系、投影
  • 样式: sld、kml

三维

  • 数据: 点云、glTF
  • 图形: OpenGL、WebGL、DirectX

WebGIS

  • 协议: WMS、WTMS、WFS、WCS、WPS、XYZ

数据科学

  • 空间分析: 数据格式、算法
  • 大数据: Hadoop、Kafka

图形

  • 图形学: 物理渲染、Shader、图形硬件
  • 渲染引擎: Unity、UE4