Fix gis extent calculation

Signed-off-by: Maximilian Krög <maxi_kroeg@web.de>
This commit is contained in:
Maximilian Krög 2023-03-01 02:16:17 +01:00 committed by Maurício Meneghini Fauth
parent b91b1c25bc
commit c5343eeed9
13 changed files with 98 additions and 51 deletions

View File

@ -21,11 +21,20 @@ use function sprintf;
use function str_replace;
use function trim;
use const INF;
/**
* Base class for all GIS data type classes.
*/
abstract class GisGeometry
{
public const EMPTY_EXTENT = [
'minX' => +INF,
'minY' => +INF,
'maxX' => -INF,
'maxY' => -INF,
];
/**
* Prepares and returns the code related to a row in the GIS dataset as SVG.
*
@ -143,8 +152,10 @@ abstract class GisGeometry
*
* @param string $point_set point set
* @param array $min_max existing min, max values
* @psalm-param array{minX:float,minY:float,maxX:float,maxY:float} $min_max *
*
* @return array the updated min, max values
* @psalm-return array{minX:float,minY:float,maxX:float,maxY:float}
*/
protected function setMinMax($point_set, array $min_max)
{

View File

@ -54,7 +54,7 @@ class GisGeometryCollection extends GisGeometry
*/
public function scaleRow($spatial)
{
$min_max = [];
$min_max = GisGeometry::EMPTY_EXTENT;
// Trim to remove leading 'GEOMETRYCOLLECTION(' and trailing ')'
$goem_col = mb_substr($spatial, 19, -1);

View File

@ -52,13 +52,14 @@ class GisLineString extends GisGeometry
* @param string $spatial spatial data of a row
*
* @return array an array containing the min, max values for x and y coordinates
* @psalm-return array{minX:float,minY:float,maxX:float,maxY:float}
*/
public function scaleRow($spatial)
{
// Trim to remove leading 'LINESTRING(' and trailing ')'
$linestring = mb_substr($spatial, 11, -1);
return $this->setMinMax($linestring, []);
return $this->setMinMax($linestring, GisGeometry::EMPTY_EXTENT);
}
/**

View File

@ -53,10 +53,11 @@ class GisMultiLineString extends GisGeometry
* @param string $spatial spatial data of a row
*
* @return array an array containing the min, max values for x and y coordinates
* @psalm-return array{minX:float,minY:float,maxX:float,maxY:float}
*/
public function scaleRow($spatial)
{
$min_max = [];
$min_max = GisGeometry::EMPTY_EXTENT;
// Trim to remove leading 'MULTILINESTRING((' and trailing '))'
$multilinestirng = mb_substr($spatial, 17, -2);

View File

@ -52,13 +52,14 @@ class GisMultiPoint extends GisGeometry
* @param string $spatial spatial data of a row
*
* @return array an array containing the min, max values for x and y coordinates
* @psalm-return array{minX:float,minY:float,maxX:float,maxY:float}
*/
public function scaleRow($spatial)
{
// Trim to remove leading 'MULTIPOINT(' and trailing ')'
$multipoint = mb_substr($spatial, 11, -1);
return $this->setMinMax($multipoint, []);
return $this->setMinMax($multipoint, GisGeometry::EMPTY_EXTENT);
}
/**

View File

@ -56,10 +56,11 @@ class GisMultiPolygon extends GisGeometry
* @param string $spatial spatial data of a row
*
* @return array an array containing the min, max values for x and y coordinates
* @psalm-return array{minX:float,minY:float,maxX:float,maxY:float}
*/
public function scaleRow($spatial)
{
$min_max = [];
$min_max = GisGeometry::EMPTY_EXTENT;
// Trim to remove leading 'MULTIPOLYGON(((' and trailing ')))'
$multipolygon = mb_substr($spatial, 15, -3);

View File

@ -51,13 +51,14 @@ class GisPoint extends GisGeometry
* @param string $spatial spatial data of a row
*
* @return array an array containing the min, max values for x and y coordinates
* @psalm-return array{minX:float,minY:float,maxX:float,maxY:float}
*/
public function scaleRow($spatial)
{
// Trim to remove leading 'POINT(' and trailing ')'
$point = mb_substr($spatial, 6, -1);
return $this->setMinMax($point, []);
return $this->setMinMax($point, GisGeometry::EMPTY_EXTENT);
}
/**

View File

@ -59,6 +59,7 @@ class GisPolygon extends GisGeometry
* @param string $spatial spatial data of a row
*
* @return array an array containing the min, max values for x and y coordinates
* @psalm-return array{minX:float,minY:float,maxX:float,maxY:float}
*/
public function scaleRow($spatial)
{
@ -74,7 +75,7 @@ class GisPolygon extends GisGeometry
$ring = $parts[0];
}
return $this->setMinMax($ring, []);
return $this->setMinMax($ring, GisGeometry::EMPTY_EXTENT);
}
/**

View File

@ -17,6 +17,7 @@ use function array_merge;
use function base64_encode;
use function count;
use function intval;
use function is_finite;
use function is_numeric;
use function is_string;
use function mb_strlen;
@ -543,12 +544,7 @@ class GisVisualization
*/
private function scaleDataSet(array $data)
{
$min_max = [
'maxX' => 0.0,
'maxY' => 0.0,
'minX' => 0.0,
'minY' => 0.0,
];
$min_max = GisGeometry::EMPTY_EXTENT;
$border = 15;
// effective width and height of the plot
$plot_width = $this->settings['width'] - 2 * $border;
@ -577,28 +573,35 @@ class GisVisualization
// Update minimum/maximum values for x and y coordinates.
$c_maxX = (float) $scale_data['maxX'];
if ($min_max['maxX'] === 0.0 || $c_maxX > $min_max['maxX']) {
if ($c_maxX > $min_max['maxX']) {
$min_max['maxX'] = $c_maxX;
}
$c_minX = (float) $scale_data['minX'];
if ($min_max['minX'] === 0.0 || $c_minX < $min_max['minX']) {
if ($c_minX < $min_max['minX']) {
$min_max['minX'] = $c_minX;
}
$c_maxY = (float) $scale_data['maxY'];
if ($min_max['maxY'] === 0.0 || $c_maxY > $min_max['maxY']) {
if ($c_maxY > $min_max['maxY']) {
$min_max['maxY'] = $c_maxY;
}
$c_minY = (float) $scale_data['minY'];
if ($min_max['minY'] !== 0.0 && $c_minY >= $min_max['minY']) {
if ($c_minY >= $min_max['minY']) {
continue;
}
$min_max['minY'] = $c_minY;
}
if (! is_finite($min_max['minX']) || ! is_finite($min_max['minY'])) {
$min_max['maxX'] = 0.0;
$min_max['maxY'] = 0.0;
$min_max['minX'] = 0.0;
$min_max['minY'] = 0.0;
}
// scale the visualization
$x_ratio = ($min_max['maxX'] - $min_max['minX']) / $plot_width;
$y_ratio = ($min_max['maxY'] - $min_max['minY']) / $plot_height;

View File

@ -3746,12 +3746,22 @@ parameters:
path: libraries/classes/Gis/GisGeometry.php
-
message: "#^Method PhpMyAdmin\\\\Gis\\\\GisGeometry\\:\\:setMinMax\\(\\) has parameter \\$min_max with no value type specified in iterable type array\\.$#"
message: "#^Offset 'maxX' on array\\{minX\\: float, minY\\: float, maxX\\: float, maxY\\: float\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: libraries/classes/Gis/GisGeometry.php
-
message: "#^Method PhpMyAdmin\\\\Gis\\\\GisGeometry\\:\\:setMinMax\\(\\) return type has no value type specified in iterable type array\\.$#"
message: "#^Offset 'maxY' on array\\{minX\\: float, minY\\: float, maxX\\: float, maxY\\: float\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: libraries/classes/Gis/GisGeometry.php
-
message: "#^Offset 'minX' on array\\{minX\\: float, minY\\: float, maxX\\: float, maxY\\: float\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: libraries/classes/Gis/GisGeometry.php
-
message: "#^Offset 'minY' on array\\{minX\\: float, minY\\: float, maxX\\: float, maxY\\: float\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: libraries/classes/Gis/GisGeometry.php
@ -3805,6 +3815,26 @@ parameters:
count: 1
path: libraries/classes/Gis/GisGeometryCollection.php
-
message: "#^Offset 'maxX' on array\\{minX\\: float, minY\\: float, maxX\\: float, maxY\\: float\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: libraries/classes/Gis/GisGeometryCollection.php
-
message: "#^Offset 'maxY' on array\\{minX\\: float, minY\\: float, maxX\\: float, maxY\\: float\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: libraries/classes/Gis/GisGeometryCollection.php
-
message: "#^Offset 'minX' on array\\{minX\\: float, minY\\: float, maxX\\: float, maxY\\: float\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: libraries/classes/Gis/GisGeometryCollection.php
-
message: "#^Offset 'minY' on array\\{minX\\: float, minY\\: float, maxX\\: float, maxY\\: float\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: libraries/classes/Gis/GisGeometryCollection.php
-
message: "#^Static property PhpMyAdmin\\\\Gis\\\\GisGeometryCollection\\:\\:\\$instance \\(PhpMyAdmin\\\\Gis\\\\GisGeometryCollection\\) in isset\\(\\) is not nullable\\.$#"
count: 1
@ -3845,11 +3875,6 @@ parameters:
count: 1
path: libraries/classes/Gis/GisLineString.php
-
message: "#^Method PhpMyAdmin\\\\Gis\\\\GisLineString\\:\\:scaleRow\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: libraries/classes/Gis/GisLineString.php
-
message: "#^Parameter \\#5 \\$color of method PhpMyAdmin\\\\Image\\\\ImageWrapper\\:\\:line\\(\\) expects int, int\\|false given\\.$#"
count: 1
@ -3905,11 +3930,6 @@ parameters:
count: 1
path: libraries/classes/Gis/GisMultiLineString.php
-
message: "#^Method PhpMyAdmin\\\\Gis\\\\GisMultiLineString\\:\\:scaleRow\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: libraries/classes/Gis/GisMultiLineString.php
-
message: "#^Parameter \\#5 \\$color of method PhpMyAdmin\\\\Image\\\\ImageWrapper\\:\\:line\\(\\) expects int, int\\|false given\\.$#"
count: 1
@ -3970,11 +3990,6 @@ parameters:
count: 1
path: libraries/classes/Gis/GisMultiPoint.php
-
message: "#^Method PhpMyAdmin\\\\Gis\\\\GisMultiPoint\\:\\:scaleRow\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: libraries/classes/Gis/GisMultiPoint.php
-
message: "#^Parameter \\#5 \\$color of method PhpMyAdmin\\\\Image\\\\ImageWrapper\\:\\:string\\(\\) expects int, int\\|false given\\.$#"
count: 1
@ -4035,11 +4050,6 @@ parameters:
count: 1
path: libraries/classes/Gis/GisMultiPolygon.php
-
message: "#^Method PhpMyAdmin\\\\Gis\\\\GisMultiPolygon\\:\\:scaleRow\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: libraries/classes/Gis/GisMultiPolygon.php
-
message: "#^Parameter \\#2 \\$color of method PhpMyAdmin\\\\Image\\\\ImageWrapper\\:\\:filledPolygon\\(\\) expects int, int\\|false given\\.$#"
count: 1
@ -4095,11 +4105,6 @@ parameters:
count: 1
path: libraries/classes/Gis/GisPoint.php
-
message: "#^Method PhpMyAdmin\\\\Gis\\\\GisPoint\\:\\:scaleRow\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: libraries/classes/Gis/GisPoint.php
-
message: "#^Parameter \\#5 \\$color of method PhpMyAdmin\\\\Image\\\\ImageWrapper\\:\\:string\\(\\) expects int, int\\|false given\\.$#"
count: 1
@ -4185,11 +4190,6 @@ parameters:
count: 1
path: libraries/classes/Gis/GisPolygon.php
-
message: "#^Method PhpMyAdmin\\\\Gis\\\\GisPolygon\\:\\:scaleRow\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: libraries/classes/Gis/GisPolygon.php
-
message: "#^Parameter \\#2 \\$color of method PhpMyAdmin\\\\Image\\\\ImageWrapper\\:\\:filledPolygon\\(\\) expects int, int\\|false given\\.$#"
count: 1

View File

@ -15640,7 +15640,8 @@
</NonInvariantDocblockPropertyType>
</file>
<file src="test/classes/Gis/GisVisualizationTest.php">
<MixedAssignment occurrences="9">
<MixedAssignment occurrences="10">
<code>$dataSet</code>
<code>$dataSet</code>
<code>$queryString</code>
<code>$queryString</code>

View File

@ -71,7 +71,7 @@ class GisGeometryTest extends AbstractTestCase
return [
[
'12 35,48 75,69 23,25 45,14 53,35 78',
[],
GisGeometry::EMPTY_EXTENT,
[
'minX' => 12,
'maxX' => 69,

View File

@ -80,6 +80,32 @@ class GisVisualizationTest extends AbstractTestCase
],
$dataSet
);
// Regression test for bug with 0.0 sentinel values
$dataSet = $this->callFunction(
$gis,
GisVisualization::class,
'scaleDataSet',
[
[
['abc' => 'MULTIPOLYGON(((0 0,0 3,3 3,3 0,0 0),(1 1,1 2,2 2,2 1,1 1)))'],
['abc' => 'MULTIPOLYGON(((10 10,10 13,13 13,13 10,10 10),(11 11,11 12,12 12,12 11,11 11)))'],
],
]
);
$this->assertSame(
[
'scale' => 32.30769230769231,
'x' => -2.7857142857142865,
'y' => -0.4642857142857143,
'minX' => 0.0,
'maxX' => 13.0,
'minY' => 0.0,
'maxY' => 13.0,
'height' => 450,
],
$dataSet
);
}
/**