1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace PhpMyAdmin\Utils; |
6
|
|
|
|
7
|
|
|
use PhpMyAdmin\DatabaseInterface; |
8
|
|
|
|
9
|
|
|
use function array_map; |
10
|
|
|
use function bin2hex; |
11
|
|
|
use function mb_strtolower; |
12
|
|
|
use function mb_strtoupper; |
13
|
|
|
use function preg_match; |
14
|
|
|
use function trim; |
15
|
|
|
|
16
|
|
|
final class Gis |
17
|
|
|
{ |
18
|
|
|
/** |
19
|
|
|
* Converts GIS data to Well Known Text format |
20
|
|
|
* |
21
|
|
|
* @param string $data GIS data |
22
|
|
|
* @param bool $includeSRID Add SRID to the WKT |
23
|
|
|
* |
24
|
|
|
* @return string GIS data in Well Know Text format |
25
|
|
|
*/ |
26
|
32 |
|
public static function convertToWellKnownText(string $data, bool $includeSRID = false): string |
27
|
|
|
{ |
28
|
|
|
// Convert to WKT format |
29
|
32 |
|
$hex = bin2hex($data); |
30
|
32 |
|
$spatialAsText = 'ASTEXT'; |
31
|
32 |
|
$spatialSrid = 'SRID'; |
32
|
32 |
|
$axisOrder = ''; |
33
|
32 |
|
$dbi = DatabaseInterface::getInstance(); |
|
|
|
|
34
|
32 |
|
$mysqlVersionInt = $dbi->getVersion(); |
35
|
32 |
|
if ($mysqlVersionInt >= 50600) { |
36
|
24 |
|
$spatialAsText = 'ST_ASTEXT'; |
37
|
24 |
|
$spatialSrid = 'ST_SRID'; |
38
|
|
|
} |
39
|
|
|
|
40
|
32 |
|
if ($mysqlVersionInt >= 80001 && ! $dbi->isMariaDB()) { |
41
|
8 |
|
$axisOrder = ', \'axis-order=long-lat\''; |
42
|
|
|
} |
43
|
|
|
|
44
|
32 |
|
$wktsql = 'SELECT ' . $spatialAsText . "(x'" . $hex . "'" . $axisOrder . ')'; |
45
|
32 |
|
if ($includeSRID) { |
46
|
16 |
|
$wktsql .= ', ' . $spatialSrid . "(x'" . $hex . "')"; |
47
|
|
|
} |
48
|
|
|
|
49
|
32 |
|
$wktresult = $dbi->tryQuery($wktsql); |
50
|
32 |
|
$wktarr = []; |
51
|
32 |
|
if ($wktresult) { |
52
|
32 |
|
$wktarr = $wktresult->fetchRow(); |
53
|
|
|
} |
54
|
|
|
|
55
|
32 |
|
$wktval = $wktarr[0] ?? ''; |
56
|
|
|
|
57
|
32 |
|
if ($includeSRID) { |
58
|
16 |
|
$srid = $wktarr[1] ?? null; |
59
|
16 |
|
$wktval = "'" . $wktval . "'," . $srid; |
60
|
|
|
} |
61
|
|
|
|
62
|
32 |
|
return $wktval; |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Return GIS data types |
67
|
|
|
* |
68
|
|
|
* @param bool $upperCase whether to return values in upper case |
69
|
|
|
* |
70
|
|
|
* @return string[] GIS data types |
71
|
|
|
*/ |
72
|
|
|
public static function getDataTypes(bool $upperCase = false): array |
73
|
|
|
{ |
74
|
|
|
$gisDataTypes = [ |
75
|
|
|
'geometry', |
76
|
|
|
'point', |
77
|
|
|
'linestring', |
78
|
|
|
'polygon', |
79
|
|
|
'multipoint', |
80
|
|
|
'multilinestring', |
81
|
|
|
'multipolygon', |
82
|
|
|
'geometrycollection', |
83
|
|
|
]; |
84
|
|
|
if ($upperCase) { |
85
|
|
|
return array_map(mb_strtoupper(...), $gisDataTypes); |
|
|
|
|
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
return $gisDataTypes; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Generates GIS data based on the string passed. |
93
|
|
|
* |
94
|
|
|
* @param string $gisString GIS string |
95
|
|
|
* @param int $mysqlVersion The mysql version as int |
96
|
|
|
* |
97
|
|
|
* @return string GIS data enclosed in 'ST_GeomFromText' or 'GeomFromText' function |
98
|
|
|
*/ |
99
|
8 |
|
public static function createData(string $gisString, int $mysqlVersion): string |
100
|
|
|
{ |
101
|
8 |
|
$geomFromText = $mysqlVersion >= 50600 ? 'ST_GeomFromText' : 'GeomFromText'; |
102
|
8 |
|
$gisString = trim($gisString); |
103
|
8 |
|
$geomTypes = '(POINT|MULTIPOINT|LINESTRING|MULTILINESTRING|POLYGON|MULTIPOLYGON|GEOMETRYCOLLECTION)'; |
104
|
8 |
|
if (preg_match("/^'" . $geomTypes . "\(.*\)',[0-9]*$/i", $gisString)) { |
105
|
8 |
|
return $geomFromText . '(' . $gisString . ')'; |
106
|
|
|
} |
107
|
|
|
|
108
|
8 |
|
if (preg_match('/^' . $geomTypes . '\(.*\)$/i', $gisString)) { |
109
|
|
|
return $geomFromText . "('" . $gisString . "')"; |
110
|
|
|
} |
111
|
|
|
|
112
|
8 |
|
return $gisString; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Returns the names and details of the functions |
117
|
|
|
* that can be applied on geometry data types. |
118
|
|
|
* |
119
|
|
|
* @param string|null $geomType if provided the output is limited to the functions |
120
|
|
|
* that are applicable to the provided geometry type. |
121
|
|
|
* @param bool $binary if set to false functions that take two geometries |
122
|
|
|
* as arguments will not be included. |
123
|
|
|
* @param bool $display if set to true separators will be added to the |
124
|
|
|
* output array. |
125
|
|
|
* |
126
|
|
|
* @return array<int|string,array<string,int|string>> names and details of the functions that can be applied on |
127
|
|
|
* geometry data types. |
128
|
|
|
*/ |
129
|
4 |
|
public static function getFunctions( |
130
|
|
|
string|null $geomType = null, |
131
|
|
|
bool $binary = true, |
132
|
|
|
bool $display = false, |
133
|
|
|
): array { |
134
|
4 |
|
$funcs = []; |
135
|
4 |
|
if ($display) { |
136
|
|
|
$funcs[] = ['display' => ' ']; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
// Unary functions common to all geometry types |
140
|
4 |
|
$funcs['Dimension'] = ['params' => 1, 'type' => 'int']; |
141
|
4 |
|
$funcs['Envelope'] = ['params' => 1, 'type' => 'Polygon']; |
142
|
4 |
|
$funcs['GeometryType'] = ['params' => 1, 'type' => 'text']; |
143
|
4 |
|
$funcs['SRID'] = ['params' => 1, 'type' => 'int']; |
144
|
4 |
|
$funcs['IsEmpty'] = ['params' => 1, 'type' => 'int']; |
145
|
4 |
|
$funcs['IsSimple'] = ['params' => 1, 'type' => 'int']; |
146
|
|
|
|
147
|
4 |
|
$geomType = mb_strtolower(trim((string) $geomType)); |
148
|
4 |
|
if ($display && $geomType !== 'geometry' && $geomType !== 'multipoint') { |
149
|
|
|
$funcs[] = ['display' => '--------']; |
150
|
|
|
} |
151
|
|
|
|
152
|
4 |
|
$spatialPrefix = ''; |
153
|
4 |
|
if (DatabaseInterface::getInstance()->getVersion() >= 50601) { |
|
|
|
|
154
|
|
|
// If MySQL version is greater than or equal 5.6.1, |
155
|
|
|
// use the ST_ prefix. |
156
|
4 |
|
$spatialPrefix = 'ST_'; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
// Unary functions that are specific to each geometry type |
160
|
4 |
|
if ($geomType === 'point') { |
161
|
|
|
$funcs[$spatialPrefix . 'X'] = ['params' => 1, 'type' => 'float']; |
162
|
|
|
$funcs[$spatialPrefix . 'Y'] = ['params' => 1, 'type' => 'float']; |
163
|
4 |
|
} elseif ($geomType === 'linestring') { |
164
|
|
|
$funcs['EndPoint'] = ['params' => 1, 'type' => 'point']; |
165
|
|
|
$funcs['GLength'] = ['params' => 1, 'type' => 'float']; |
166
|
|
|
$funcs['NumPoints'] = ['params' => 1, 'type' => 'int']; |
167
|
|
|
$funcs['StartPoint'] = ['params' => 1, 'type' => 'point']; |
168
|
|
|
$funcs['IsRing'] = ['params' => 1, 'type' => 'int']; |
169
|
4 |
|
} elseif ($geomType === 'multilinestring') { |
170
|
|
|
$funcs['GLength'] = ['params' => 1, 'type' => 'float']; |
171
|
|
|
$funcs['IsClosed'] = ['params' => 1, 'type' => 'int']; |
172
|
4 |
|
} elseif ($geomType === 'polygon') { |
173
|
|
|
$funcs['Area'] = ['params' => 1, 'type' => 'float']; |
174
|
|
|
$funcs['ExteriorRing'] = ['params' => 1, 'type' => 'linestring']; |
175
|
|
|
$funcs['NumInteriorRings'] = ['params' => 1, 'type' => 'int']; |
176
|
4 |
|
} elseif ($geomType === 'multipolygon') { |
177
|
|
|
$funcs['Area'] = ['params' => 1, 'type' => 'float']; |
178
|
|
|
$funcs['Centroid'] = ['params' => 1, 'type' => 'point']; |
179
|
|
|
// Not yet implemented in MySQL |
180
|
|
|
//$funcs['PointOnSurface'] = array('params' => 1, 'type' => 'point'); |
181
|
4 |
|
} elseif ($geomType === 'geometrycollection') { |
182
|
|
|
$funcs['NumGeometries'] = ['params' => 1, 'type' => 'int']; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
// If we are asked for binary functions as well |
186
|
4 |
|
if ($binary) { |
187
|
|
|
// section separator |
188
|
4 |
|
if ($display) { |
189
|
|
|
$funcs[] = ['display' => '--------']; |
190
|
|
|
} |
191
|
|
|
|
192
|
4 |
|
$funcs[$spatialPrefix . 'Crosses'] = ['params' => 2, 'type' => 'int']; |
193
|
4 |
|
$funcs[$spatialPrefix . 'Contains'] = ['params' => 2, 'type' => 'int']; |
194
|
4 |
|
$funcs[$spatialPrefix . 'Disjoint'] = ['params' => 2, 'type' => 'int']; |
195
|
4 |
|
$funcs[$spatialPrefix . 'Equals'] = ['params' => 2, 'type' => 'int']; |
196
|
4 |
|
$funcs[$spatialPrefix . 'Intersects'] = ['params' => 2, 'type' => 'int']; |
197
|
4 |
|
$funcs[$spatialPrefix . 'Overlaps'] = ['params' => 2, 'type' => 'int']; |
198
|
4 |
|
$funcs[$spatialPrefix . 'Touches'] = ['params' => 2, 'type' => 'int']; |
199
|
4 |
|
$funcs[$spatialPrefix . 'Within'] = ['params' => 2, 'type' => 'int']; |
200
|
|
|
|
201
|
4 |
|
if ($display) { |
202
|
|
|
$funcs[] = ['display' => '--------']; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
// Minimum bounding rectangle functions |
206
|
4 |
|
$funcs['MBRContains'] = ['params' => 2, 'type' => 'int']; |
207
|
4 |
|
$funcs['MBRDisjoint'] = ['params' => 2, 'type' => 'int']; |
208
|
4 |
|
$funcs['MBREquals'] = ['params' => 2, 'type' => 'int']; |
209
|
4 |
|
$funcs['MBRIntersects'] = ['params' => 2, 'type' => 'int']; |
210
|
4 |
|
$funcs['MBROverlaps'] = ['params' => 2, 'type' => 'int']; |
211
|
4 |
|
$funcs['MBRTouches'] = ['params' => 2, 'type' => 'int']; |
212
|
4 |
|
$funcs['MBRWithin'] = ['params' => 2, 'type' => 'int']; |
213
|
|
|
} |
214
|
|
|
|
215
|
4 |
|
return $funcs; |
216
|
|
|
} |
217
|
|
|
} |
218
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.