SFA-SQL 表达式构建器

0.2.0 2024-05-27 20:44 UTC

This package is auto-updated.

Last update: 2024-08-28 21:37:06 UTC


README

使用 SFA-CA 语法构建 SFA-SQL (SQL/MM) 表达式。

注意 目前此文档不仅包含此包和文档草稿的描述,还包括计划和研究成果。工作中。未发布。

简单特性 是由开放地理空间联盟制定的标准(实际上是多个标准),用于定义地理空间数据模型。该标准定义了面向对象和 SQL 访问(称为 SFA-SQL、SQL/MM 和 ArcSDE 等)到这些模型。此包在 PHP 中实现了面向对象的 SFA API,并从中生成 SFA-SQL 表达式。

可用于构建支持 SFA-SQL 或 SQL-MM 的系统(如 PostGIS、SpatiaLite、MySQL 空间等)的 SQL 语句。

特性示例

此库中的对象允许通过链式调用 SFA 创建 SQL 表达式

$geometry = new Geometry('ST_MakePoint(?, ?)', [3, 5]);
$another = new Geometry("'LINESTRING ( 2 0, 0 2 )'::geometry");

$envelope = $geometry->envelope();

(string) $envelope; // ST_Envelope(ST_MakePoint(?, ?))
$envelope->bindings; // [3, 5]

$expression = $geometry
	->union($another)
	->buffer($geometry->distance($another))
	->buffer(5.2)
	->convexHull();

(string) $expression; // ST_ConvexHull(ST_Buffer(ST_Buffer(ST_Union(ST_MakePoint(?, ?), 'LINESTRING ( 2 0, 0 2 )'::geometry), ST_Distance(ST_MakePoint(?, ?), ST_MakePoint(1, 1))), ?))
$expression->bindings; // [3, 5, 3, 5, 5.2]

您还可以指定列

$geom = new Geometry('the_geom');
$srid = $geom->srid();
(string) $srid; // ST_SRID(the_geom);

// alternate syntaxes to retrieve the SQL expression:
$srid->sql; // ST_SRID(the_geom);
$srid->__toString(); // ST_SRID(the_geom);

通过在 Expression 对象中包装原始表达式,可以防止它们进入绑定

$geom = new Geometry('geom');

// Works as expected
$bufferSize = new Expression('3 + 5');
$buf1 = $geom->buffer($bufferSize);
(string) $buf1; // ST_Buffer(geom, 3 + 5)

// Expressions can also have bindings
$dynamicBuffer = new Expression('bufpad + ?', [3]);
$buf2 = $geom->buffer($dynamicBuffer);
(string) $buf2; // ST_Buffer(geom, bufpad + ?)
$buf2->bindings; // [3]

使用构造函数初始化对象

Sfc::pointFromText('POINT(-71.064544 42.28787)')->X();
// Produces ST_X(ST_PointFromText(?)) with binding 'POINT(-71.064544 42.28787)'

使用特定于语法的构造函数和方法

PostGIS\Sfc::makePoint(1, 3)->setSRID(3059);
// Produces ST_SetSRID(ST_MakePoint(?, ?), ?) with bindings [1, 3, 3059]

SpatiaLite\Sfc::makePoint(1, 3)->setSRID(3059);
// Produces SetSRID(MakePoint(?, ?), ?) with bindings [1, 3, 3059]

使用带有设置驱动程序的工厂

// simple feature classes
$sf = new Sf('PostGIS');
$sf->point('mycol'); // a PostGIS/Point wrapping the `mycol`

// simple feature constructors
$sfc = new Sfc('PostGIS');
$sfc->makePoint(1, 3); // a PostGIS/Point with ST_MakePoint(?, ?) and [1, 3] bindings

如果无法使用绑定,可以使用引号模式

// specify a quoter
Expression::setQuoter($myPdoInstance->quote(...));

// use as normally, bindings will always be empty
Sfc::makePoint(1, 3)->setSRID(3059); // ST_SetSRID(ST_MakePoint(1, 3), 3059)

使用示例

本节以 PDO 中的纯示例展示一些示例。

查询列 yardroad 之间的关系

$yard = new Geometry('yard');
$road = new Geometry('road');

$expr = $yard->intersects($road);

$statement = $pdo->prepare("
	SELECT *
	FROM features
	WHERE $expr
");

$statement->execute($expr->bindings);

对原始语句进行列查询

$box = Geometry::fromMethod('ST_MakeEnvelope', 0, 0, 1, 3);

// You may skip trivial `new Geometry('geom')` and just supply the column name
// or any other SQL string.
$contains = $box->contains('geom');
// produces ST_Contains(ST_MakeEnvelope(?, ?, ?, ?), geom)

$statement = $pdo->prepare("
	SELECT *
	FROM features
	WHERE
		created_at > ? 
		AND $contains
");

// merge bindings with other bindings
$statement->execute([$createdFrom, ...$contains->bindings]);

设置值

$point = new Point('ST_MakePoint(?, ?)', [2, 7]);
// Equivalent to:
// $point = Point::fromMethod('ST_MakePoint', [2, 7]);

$statement = $pdo->prepare("
	UPDATE features
	SET location = $point
	WHERE id = ?
");

// merge bindings with other bindings
$statement->execute([...$point->bindings, $id]);

范围和目标

主要目标是支持创建遵循 ArcSDE 实现的 PostGIS 表达式。如果方法名称不同,我们应该有别名来支持 PostGIS 命名。如果参数不同,我们应该努力支持所有情况。

MySQL、MariaDB 和 SpatiaLite 的支持也很好。

然而,到目前为止,这不应该是一个数据库之间的兼容层。如果同一函数在不同数据库上执行不同的操作,我们不会统一行为。我们只是让您以原样调用该函数。

统一层可能很有用,但这将是另一步,在原始构建器之后。

路线图

  • 文档内部(类、特性和合同建模;callFromMethod、wrap 等)
  • 所有 SFA 模型
  • 所有其他 SFA-SQL 函数
  • 对返回类型和参数类型的内部支持(例如 IntExpressionAreaExpression
  • PostGIS 特定内容,如构造函数、附加参数、地理空间内容等
  • 测试支持,包括 SpatiaLite 驱动程序和替换“驱动程序”的能力
  • Laravel 查询构建器和 Eloquent(类型转换、设置、查询...)集成
  • MariaDB 支持

架构

一些实现细节,以帮助组织内容。

表达式

主要构建块是 Expression 类,它表示带有绑定的 SQL 表达式。所有几何类都是 Expression 的子类。所有表达式都有两个公共属性,并且它们是可转换为字符串的。

$expression->sql; // string
$expression->bindings; // array
(string) $expression; // same as $expression->sql

创建任何表达式对象有三种方式

// the constructor allows specifying sql and optionally bindings
new Expression('mycolumn');
new Expression('count(mycolumn)');
new Expression('? + ?', [1, 3]);
// You can also instantiate any subclass of expression
// here's an expression that refers to your location column and knows that
// it represents a point-typed value
new Point('the_location');

// the make factory can instantiate an expression as well
Expression::make('mycolumn');
// it can wrap an existing expression to cast it to another type:
Geometry::make($someExpression); // return a Geometry with the same $sql and $bindings as $someExpression has
// the goal is to enforce type — it only accepts strings or subclasses
// i.e. the argument of Expression::make is covariant and violates Liskov substitution principle
Geometry::make(new Geometry('the_location')); // ok
Geometry::make(new Point('the_location')); // ok, point can be treated as geometry
Point::make(new Geometry('the_location')); // throws InvalidExpressionType — arbitrary geometry can't be treated as a point
Geometry::make('mygeom'); // ok, we trust you know what you're doing

// the fromMethod factory constructs an SQL statement calling a function
Expression::fromMethod('VERSION'); // makes an expression with $sql equal to VERSION()
// it puts raw args as bindings
Point::fromMethod('ST_MakePoint', 23, 56); // $sql is ST_MakeLine(?, ?) and $bindings is [23, 56]
// expression args are inserted as args
LineString::fromMethod('ST_MakeLine', new Point('start_point_col'), new Point('end_point_col')); // $sql is ST_MakeLine(start_point_col, end_point_col)
// expression arg bindings are merged
$point1 = Point::fromMethod('POINT', 23, 56);
$point2 = Point::fromMethod('POINT', 23, 57);
LineString::fromMethod('ST_MakeLine', $point1, $point2); // $sql is ST_MakeLine(POINT(?, ?), POINT(?, ?)) and $bindings is [23, 56, 23, 57]

表达式子类

表达式子类的目的是了解我们正在处理的对象类型以及它上面的可用方法。

$point = new Point('some_col');
$point->x(); // an Expression with $sql equal to ST_X(some_col)

$line = new LineString('line_column');
$endPoint = $line->pointN(-1); // a Point with $sql = ST_PointN(line_column, ?) and $bindings [-1]
$endPoint->x(); // and expression with $sql = ST_X(ST_PointN(line_column, ?)) and $bindings = [-1]
$endPoint->pointN(-1); // throws exception as you cant ST_PointN on a point

目前我们只有几何表达式的表达式子类,但以后可能会有其他类型(整数值表达式、面积值表达式等)。

几何类

除了基本的 Expression 工具之外,Geometry(以及所有 GIS 子类)还有以下辅助工具

// Wrap the expression in a function call
$myGeometry = new Geometry('col');
$myGeometry->wrap('COUNT'); // COUNT(col)

// Query the geometry against another geometry
$myGeometry = new Point('start');
$myGeometry->query('ST_Distance', new Point('end')); // Expression holding ST_Distance(start, end)
// This also autowraps the second arg as a geometry if you pass a string
$myGeometry->query('ST_Distance', 'end'); // Expression holding ST_Distance(start, end)

// Combine the geometry with another
$point = new Point('start');
$point->combine('ST_Union', 'end'); // Geometry holding ST_Union(start, end)
// The only difference is that ->query() returns a base Expression, but ->combine() returns a Geometry

层次结构和组合

在OGC规范中定义的几何类层次结构及其所需方法定义在OGC\Contracts接口中。

OGC规范中描述的OOP方法到SQL的默认映射实现于OGC\Traits中。

类构建的方式大致如下(伪代码)

class PostGIS\Line extends PostGIS\LineString implements OGC\Contracts\Line
{
	// default implementation
	use OGC\Traits\Line;

	// PostGIS-specific overrides and additions
	public function someLineMethod($anArg)
	{
		//
	}
}

我们还包含在OGC命名空间中的类,这些类实现了规范中定义的所有内容。看起来没有数据库管理系统(DBMS)能够完全按照这种方式实现,但现在先不考虑这一点。

顺便说一句,如果特定DBMS没有实现OGC定义的方法,则会抛出MethodNotImplemented异常。

构造函数

构造函数在名为Sfc(简单特征构造函数)的类中实现。

默认值在OGC\Sfc

Sfc::pointFromText('POINT(23 56)'); // returns a Point with $sql = ST_PointFromText(?) and $bindings = ['POINT(23 56)']

以及更具体的细节在特定的Sfc

PostGIS\Sfc::makePointM(23, 56, 5); // returns a Point with $sql = ST_MakePointM(?, ?, ?) and $bindings = [23, 56, 5]

Sfc类还具有[type]FromMethod魔术方法,例如

PostGIS\Sfc::pointFromMethod(...$args); // same as PostGIS\Point::fromMethod(...$args)
MySQL\Sfc::pointFromMethod(...$args); // same as MySQL\Point::fromMethod(...$args)

这在您想要在Sfc类之间共享功能但需要返回属于特定DBMS的几何类时非常有用。

术语和参考

  • SF简单特征,几何形状及其交互接口。
  • SFA — 简单特征访问,与简单特征相同。
  • OGC — 开放地理空间联盟
  • SQL/MM — 由ISO/IEC 13249定义的SQL多媒体模式,包括在ISO/IEC 13249-3中定义的SQL/MM空间。
  • SFA-CA — OGC在SFA第1部分中定义的几何模型。
  • SFA-SQL — OGC在SFA第2部分中定义的简单特征的SQL模式。
  • ArcSDE — ArcGIS套件中与关系数据库通信的东西。一些RDBMS将其视为事实标准,有时会遵循SFA(放弃ArcSDE不使用的SFA方法https://trac.osgeo.org/postgis/changeset/8680)或SQL/MM(为ArcSDE实现而不是SQL/MM https://postgis.net.cn/docs/ST_OrderingEquals.html)。

我们的API主要尝试支持OpenGIS SFA标准,但它可能还包括一些来自ArcSDE和其他地方的一些常见别名。

支持的SQL描述在多个标准和其他资源中

有些人可能还对ISO 19125(SFA)、ISO/IEC 13249-3(SQL/MM空间)和ISO 19107(空间模式)感兴趣。

类似的项目

brick/geo

https://github.com/brick/geo

允许在PHP内部执行GIS操作,使用GEOS ext、PostGIS和其他底层引擎。包括各种数据库的SF查询引擎。

brick/geo-doctrine

https://github.com/brick/geo-doctrine

Doctrine的GIS映射和DQL的函数。

GeoPHP

https://geophp.net/

在PHP内部执行GIS操作,并允许使用WK和其他格式与数据库通信。

vincjo/spatialite

https://github.com/vincjo/spatialite

PHP对SpatiaLite的接口。

elevenlab/php-ogc

https://github.com/eleven-lab/php-ogc

在PHP中实现SFA类型。

elevenlab/laravel-geo

https://github.com/eleven-lab/laravel-geo

扩展Laravel查询构建器和Eloquent,添加一些SFA-SQL方法。

mstaack/laravel-postgis

https://github.com/mstaack/laravel-postgis

使用自定义接口实现SFA类型,并在Eloquent中支持这些对象。还添加了对这些类型在迁移中的支持。

geo-io/geometry

https://github.com/geo-io/geometry

在纯PHP中实现SFA面向对象特性。该项目是Geo I/O项目的一部分,该项目还包括其他Geo PHP工具。Geo I/O

jsor/doctrine-postgis

https://github.com/jsor/doctrine-postgis

Doctrine对PostGIS的支持,请参阅支持的功能。我们应该努力支持相同的。