Commit 6769d1a0 authored by Anel Husakovic's avatar Anel Husakovic

MDEV-13467: Feature request: Support for ST_Distance_Sphere()

- Cherry-pick 51e48b9f - vscode gitignore
- Thanks Robin Dupret for the review.

Reviewed by:daniel@mariadb.org
            holyfoot@mariadb.com
parent 48141f3c
......@@ -507,3 +507,13 @@ FakesAssemblies/
# QtCreator && CodeBlocks
*.cbp
compile_commands.json
.clang-format
.kscope/
.vimrc
.editorconfig
.kateconfig
*.kdev4
# Visual Studio Code workspace
.vscode/
......@@ -807,3 +807,114 @@ SRID(GEOMETRYFROMTEXT(' MULTIPOINT(8 4,5 0,7 8,6 9,3 4,7 3,5 5) '))));
ASTEXT(ST_BUFFER(POLYGONFROMTEXT(' POLYGON((9 9,5 2,4 5,9 9))'),
SRID(GEOMETRYFROMTEXT(' MULTIPOINT(8 4,5 0,7 8,6 9,3 4,7 3,5 5) '))))
POLYGON((9 9,5 2,4 5,9 9))
#
# MDEV-13467 Feature request: Support for ST_Distance_Sphere()
#
SELECT ST_DISTANCE_SPHERE();
ERROR 42000: Incorrect parameter count in the call to native function 'ST_DISTANCE_SPHERE'
SELECT ST_DISTANCE_SPHERE(NULL);
ERROR 42000: Incorrect parameter count in the call to native function 'ST_DISTANCE_SPHERE'
SELECT ST_DISTANCE_SPHERE(NULL, NULL);
ST_DISTANCE_SPHERE(NULL, NULL)
NULL
SELECT ST_DISTANCE_SPHERE(NULL, NULL, 3);
ST_DISTANCE_SPHERE(NULL, NULL, 3)
NULL
SELECT ST_DISTANCE_SPHERE(NULL, 1, 3);
ST_DISTANCE_SPHERE(NULL, 1, 3)
NULL
SELECT ST_DISTANCE_SPHERE(1, NULL, 3);
ST_DISTANCE_SPHERE(1, NULL, 3)
NULL
SELECT ST_DISTANCE_SPHERE(1, 1);
ERROR 22023: Invalid GIS data provided to function ST_Distance_Sphere.
SELECT ST_DISTANCE_SPHERE(1, 1, 3);
ERROR 22023: Invalid GIS data provided to function ST_Distance_Sphere.
SELECT ST_DISTANCE_SPHERE(1, 1, NULL);
ST_DISTANCE_SPHERE(1, 1, NULL)
NULL
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(1 0)'), ST_GEOMFROMTEXT('LINESTRING(0 0, 1 1)'));
ERROR HY000: Internal error: st_distance_sphere
# Test Points and radius
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('POINT(1 1)'));
ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('POINT(1 1)'))
157249.0357231545
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(-1 -1)'), ST_GEOMFROMTEXT('POINT(-2 -2)')), 10);
TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(-1 -1)'), ST_GEOMFROMTEXT('POINT(-2 -2)')), 10)
157225.0865419108
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('POINT(1 1)'), 1);
ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('POINT(1 1)'), 1)
0.024682056391766436
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('POINT(1 1)'), 0);
ERROR HY000: Internal error: Radius must be greater than zero.
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('POINT(1 1)'), -1);
ERROR HY000: Internal error: Radius must be greater than zero.
# Test longitude/lattitude
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 1)'), ST_GEOMFROMTEXT('POINT(1 2)')), 10);
TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 1)'), ST_GEOMFROMTEXT('POINT(1 2)')), 10)
157225.0865419108
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 1)'), ST_GEOMFROMTEXT('POINT(2 1)')), 10);
TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 1)'), ST_GEOMFROMTEXT('POINT(2 1)')), 10)
222355.4901806686
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(1 0)'), ST_GEOMFROMTEXT('POINT(1 2)')), 10);
TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(1 0)'), ST_GEOMFROMTEXT('POINT(1 2)')), 10)
222389.3645969269
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(1 0)'), ST_GEOMFROMTEXT('POINT(2 1)'));
ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(1 0)'), ST_GEOMFROMTEXT('POINT(2 1)'))
157249.0357231545
# Test Points - Multipoints
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(1 1)'));
ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(1 1)'))
157249.0357231545
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 1)'), ST_GEOMFROMTEXT('POINT(0 0)'));
ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 1)'), ST_GEOMFROMTEXT('POINT(0 0)'))
157249.0357231545
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(1 1,2 2)'));
ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(1 1,2 2)'))
157249.0357231545
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(2 2,1 1)'));
ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(2 2,1 1)'))
157249.0357231545
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(1 1,2 2)'), 1);
ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(1 1,2 2)'), 1)
0.024682056391766436
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(2 2,1 1)'), 1);
ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(2 2,1 1)'), 1)
0.024682056391766436
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(2 2, 1 1, 3 4)'), 1);
ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(2 2, 1 1, 3 4)'), 1)
0.024682056391766436
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(2 2, 1 1,5 6)'), 1);
ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(2 2, 1 1,5 6)'), 1)
0.024682056391766436
# Test Multipoints - Multipoints
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(3 4,8 9 )'), ST_GEOMFROMTEXT('MULTIPOINT(3 4,8 9 )'));
ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(3 4,8 9 )'), ST_GEOMFROMTEXT('MULTIPOINT(3 4,8 9 )'))
0
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 2,1 1 )'), ST_GEOMFROMTEXT('MULTIPOINT(3 4,8 9 )')), 10);
TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 2,1 1 )'), ST_GEOMFROMTEXT('MULTIPOINT(3 4,8 9 )')), 10)
314282.5644496733
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 2,1 1 )'), ST_GEOMFROMTEXT('MULTIPOINT(8 9,3 4 )')), 10);
TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 2,1 1 )'), ST_GEOMFROMTEXT('MULTIPOINT(8 9,3 4 )')), 10)
314282.5644496733
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 2,1 1 )'), ST_GEOMFROMTEXT('MULTIPOINT(8 9,3 4 )'),1), 17);
TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 2,1 1 )'), ST_GEOMFROMTEXT('MULTIPOINT(8 9,3 4 )'),1), 17)
0.04933028646581131
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 2,1 1 )'), ST_GEOMFROMTEXT('MULTIPOINT(8 9,3 4 )'),0);
ERROR HY000: Internal error: Radius must be greater than zero.
set @pt1 = ST_GeomFromText('POINT(190 -30)');
set @pt2 = ST_GeomFromText('POINT(-30 50)');
SELECT ST_Distance_Sphere(@pt1, @pt2);
ERROR HY000: Out of range error: Longitude should be [-180,180] in function ST_Distance_Sphere.
set @pt1 = ST_GeomFromText('POINT(135 -30)');
set @pt2 = ST_GeomFromText('POINT(-30 91)');
SELECT ST_Distance_Sphere(@pt1, @pt2);
ERROR HY000: Out of range error: Latitude should be [-90,90] in function ST_Distance_Sphere.
set @zenica = ST_GeomFromText('POINT(17.907743 44.203438)');
set @sarajevo = ST_GeomFromText('POINT(18.413076 43.856258)');
SELECT TRUNCATE(ST_Distance_Sphere(@zenica, @sarajevo), 10);
TRUNCATE(ST_Distance_Sphere(@zenica, @sarajevo), 10)
55878.5933759170
SELECT TRUNCATE(ST_Distance_Sphere(@sarajevo, @zenica), 10);
TRUNCATE(ST_Distance_Sphere(@sarajevo, @zenica), 10)
55878.5933759170
......@@ -393,7 +393,6 @@ insert into t1 values (1);
insert into t1 values (1.11);
--error 1416
insert into t1 values ("qwerty");
# --error ER_GIS_INVALID_DATA
--error ER_BAD_NULL_ERROR
insert into t1 values (ST_pointfromtext('point(1,1)'));
......@@ -437,7 +436,6 @@ select
ST_y(b) IS NULL
from t1;
# --error ER_GIS_INVALID_DATA
select
MBRwithin(b, b) IS NULL, MBRcontains(b, b) IS NULL, MBRoverlaps(b, b) IS NULL,
MBRequals(b, b) IS NULL, MBRdisjoint(b, b) IS NULL, ST_touches(b, b) IS NULL,
......@@ -466,7 +464,6 @@ DROP TABLE t1;
#
CREATE TABLE `t1` ( `col9` set('a'), `col89` date);
INSERT IGNORE INTO `t1` VALUES ('','0000-00-00');
# --error ER_GIS_INVALID_DATA
select ST_geomfromtext(col9,col89) as a from t1;
DROP TABLE t1;
......@@ -623,17 +620,11 @@ SELECT GROUP_CONCAT(a2.name ORDER BY a2.name) AS MBRwithin FROM t1 a1 JOIN
# MBROverlaps needs a few more tests, with point and line dimensions
# --error ER_GIS_INVALID_DATA
SET @vert1 = ST_GeomFromText('POLYGON ((0 -2, 0 2, 0 -2))');
# --error ER_GIS_INVALID_DATA
SET @horiz1 = ST_GeomFromText('POLYGON ((-2 0, 2 0, -2 0))');
# --error ER_GIS_INVALID_DATA
SET @horiz2 = ST_GeomFromText('POLYGON ((-1 0, 3 0, -1 0))');
# --error ER_GIS_INVALID_DATA
SET @horiz3 = ST_GeomFromText('POLYGON ((2 0, 3 0, 2 0))');
# --error ER_GIS_INVALID_DATA
SET @point1 = ST_GeomFromText('POLYGON ((0 0))');
# --error ER_GIS_INVALID_DATA
SET @point2 = ST_GeomFromText('POLYGON ((-2 0))');
SELECT GROUP_CONCAT(a1.name ORDER BY a1.name) AS MBRoverlaps FROM t1 a1 WHERE MBROverlaps(a1.square, @vert1) GROUP BY a1.name;
......@@ -772,10 +763,8 @@ SELECT 1 FROM (SELECT GREATEST(1,GEOMETRYCOLLECTION('00000','00000')) b FROM DUA
--echo # BUG#51875: crash when loading data into geometry function ST_polyfromwkb
--echo #
SET @a=0x00000000030000000100000000000000000000000000144000000000000014400000000000001840000000000000184000000000000014400000000000001440;
# --error ER_GIS_INVALID_DATA
SET @a=ST_POLYFROMWKB(@a);
SET @a=0x00000000030000000000000000000000000000000000144000000000000014400000000000001840000000000000184000000000000014400000000000001440;
# --error ER_GIS_INVALID_DATA
SET @a=ST_POLYFROMWKB(@a);
......@@ -899,7 +888,6 @@ DROP TABLE g1;
CREATE TABLE g1(a TEXT NOT NULL, KEY(a(255)));
INSERT INTO g1 VALUES ('a'),('a');
# --error ER_GIS_INVALID_DATA
SELECT 1 FROM g1 WHERE a >= ANY
(SELECT 1 FROM g1 WHERE a = ST_geomfromtext('') OR a) ;
......@@ -1447,5 +1435,4 @@ DROP DATABASE gis_ogs;
--echo # Bug#13362660 ASSERTION `FIELD_POS < FIELD_COUNT' FAILED. IN PROTOCOL_TEXT::STORE
--echo #
# --error ER_GIS_INVALID_DATA
SELECT ST_Union('', ''), md5(1);
......@@ -47,7 +47,6 @@ ST_GeomFromText('GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(0 0,10 10))'));
CREATE INDEX linestring_index ON linestring(linestring_nokey(5));
ALTER TABLE linestring ADD KEY (linestring_key(5));
# --error ER_GIS_INVALID_DATA
SELECT ST_AsText(linestring_nokey) FROM linestring FORCE KEY (
linestring_key ) WHERE ST_CONTAINS( ST_GeomFromText('POLYGON( ( 3923 2815 , 4246
2122 , 4028 2971 , 4017 3019 , 3923 2815 ) )') , linestring_key ) AND
......
......@@ -1141,7 +1141,6 @@ insert into `t1` values(
linestring(point(1,1),point(1,1))
);
# --error ER_GIS_INVALID_DATA
--error ER_BAD_NULL_ERROR
insert into `t1` values
(
......
......@@ -388,7 +388,6 @@ insert into t1 values (1.11);
--error 1416
insert into t1 values ("qwerty");
--error 1048
# --error ER_GIS_INVALID_DATA
insert into t1 values (ST_pointfromtext('point(1,1)'));
drop table t1;
......@@ -431,7 +430,6 @@ select
ST_y(b) IS NULL
from t1;
# --error ER_GIS_INVALID_DATA
select
MBRwithin(b, b) IS NULL, MBRcontains(b, b) IS NULL, MBRoverlaps(b, b) IS NULL,
MBRequals(b, b) IS NULL, MBRdisjoint(b, b) IS NULL, ST_touches(b, b) IS NULL,
......@@ -460,7 +458,6 @@ DROP TABLE t1;
#
CREATE TABLE `t1` ( `col9` set('a'), `col89` date);
INSERT IGNORE INTO `t1` VALUES ('','0000-00-00');
# --error ER_GIS_INVALID_DATA
select ST_geomfromtext(col9,col89) as a from t1;
DROP TABLE t1;
......@@ -617,17 +614,11 @@ SELECT GROUP_CONCAT(a2.name ORDER BY a2.name) AS MBRwithin FROM t1 a1 JOIN
# MBROverlaps needs a few more tests, with point and line dimensions
# --error ER_GIS_INVALID_DATA
SET @vert1 = ST_GeomFromText('POLYGON ((0 -2, 0 2, 0 -2))');
# --error ER_GIS_INVALID_DATA
SET @horiz1 = ST_GeomFromText('POLYGON ((-2 0, 2 0, -2 0))');
# --error ER_GIS_INVALID_DATA
SET @horiz2 = ST_GeomFromText('POLYGON ((-1 0, 3 0, -1 0))');
# --error ER_GIS_INVALID_DATA
SET @horiz3 = ST_GeomFromText('POLYGON ((2 0, 3 0, 2 0))');
# --error ER_GIS_INVALID_DATA
SET @point1 = ST_GeomFromText('POLYGON ((0 0))');
# --error ER_GIS_INVALID_DATA
SET @point2 = ST_GeomFromText('POLYGON ((-2 0))');
SELECT GROUP_CONCAT(a1.name ORDER BY a1.name) AS MBRoverlaps FROM t1 a1 WHERE MBROverlaps(a1.square, @vert1) GROUP BY a1.name;
......@@ -766,10 +757,8 @@ SELECT 1 FROM (SELECT GREATEST(1,GEOMETRYCOLLECTION('00000','00000')) b FROM DUA
--echo # BUG#51875: crash when loading data into geometry function ST_polyfromwkb
--echo #
SET @a=0x00000000030000000100000000000000000000000000144000000000000014400000000000001840000000000000184000000000000014400000000000001440;
# --error ER_GIS_INVALID_DATA
SET @a=ST_POLYFROMWKB(@a);
SET @a=0x00000000030000000000000000000000000000000000144000000000000014400000000000001840000000000000184000000000000014400000000000001440;
# --error ER_GIS_INVALID_DATA
SET @a=ST_POLYFROMWKB(@a);
......@@ -901,7 +890,6 @@ DROP TABLE g1;
CREATE TABLE g1(a TEXT NOT NULL, KEY(a(255)));
INSERT INTO g1 VALUES ('a'),('a');
# --error ER_GIS_INVALID_DATA
SELECT 1 FROM g1 WHERE a >= ANY
(SELECT 1 FROM g1 WHERE a = ST_geomfromtext('') OR a) ;
......@@ -1440,5 +1428,4 @@ DROP DATABASE gis_ogs;
--echo # Bug#13362660 ASSERTION `FIELD_POS < FIELD_COUNT' FAILED. IN PROTOCOL_TEXT::STORE
--echo #
# --error ER_GIS_INVALID_DATA
SELECT ST_Union('', ''), md5(1);
......@@ -122,7 +122,6 @@ SELECT ST_Equals(ST_PointFromText('POINT (12 13)'),ST_PointFromText('POINT (12 1
--echo # BUG#11759650/51979: UNION/INTERSECTION OF POLYGONS CRASHES MYSQL
--echo #
# --error ER_GIS_INVALID_DATA
SELECT ST_ASTEXT(ST_UNION(ST_GEOMFROMTEXT('POLYGON((525000 183300,525400
183300,525400 18370, 525000 183700,525000 183300))'),
ST_geomfromtext('POLYGON((525298.67 183511.53,525296.57
......@@ -140,7 +139,6 @@ ST_geomfromtext('POLYGON((525298.67 183511.53,525296.57
183491.55))'))) st_u;
SET @a=0x0000000001030000000200000005000000000000000000000000000000000000000000000000002440000000000000000000000000000024400000000000002440000000000000000000000000000024400000000000000000000000000000000000000000000000000000F03F000000000000F03F0000000000000040000000000000F03F00000000000000400000000000000040000000000000F03F0000000000000040000000000000F03F000000000000F03F;
# --error ER_GIS_INVALID_DATA
SELECT ST_ASTEXT(ST_TOUCHES(@a, ST_GEOMFROMTEXT('point(0 0)'))) t;
......
......@@ -78,17 +78,11 @@ SELECT name, ST_AsText(square) from t1 where MBRWithin(@p, square);
# MBROverlaps needs a few more tests, with point and line dimensions
# --error ER_GIS_INVALID_DATA
SET @vert1 = ST_GeomFromText('POLYGON ((0 -2, 0 2, 0 -2))');
# --error ER_GIS_INVALID_DATA
SET @horiz1 = ST_GeomFromText('POLYGON ((-2 0, 2 0, -2 0))');
# --error ER_GIS_INVALID_DATA
SET @horiz2 = ST_GeomFromText('POLYGON ((-1 0, 3 0, -1 0))');
# --error ER_GIS_INVALID_DATA
SET @horiz3 = ST_GeomFromText('POLYGON ((2 0, 3 0, 2 0))');
# --error ER_GIS_INVALID_DATA
SET @point1 = ST_GeomFromText('POLYGON ((0 0))');
# --error ER_GIS_INVALID_DATA
SET @point2 = ST_GeomFromText('POLYGON ((-2 0))');
SELECT GROUP_CONCAT(a1.name ORDER BY a1.name) AS MBRoverlaps FROM t1 a1 WHERE MBROverlaps(a1.square, @vert1) GROUP BY a1.name;
......
......@@ -393,3 +393,81 @@ with cte1 as( select (st_symdifference(point(1,1),point(1,1))) as a1 ), cte2 a
--source include/gis_debug.inc
--echo #
--echo # MDEV-13467 Feature request: Support for ST_Distance_Sphere()
--echo #
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT ST_DISTANCE_SPHERE();
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT ST_DISTANCE_SPHERE(NULL);
SELECT ST_DISTANCE_SPHERE(NULL, NULL);
# NULL args and optional radius will return NULL
SELECT ST_DISTANCE_SPHERE(NULL, NULL, 3);
# At least 1 NULL arg and optional radius will return NULL
SELECT ST_DISTANCE_SPHERE(NULL, 1, 3);
# At least 1 NULL arg and optional radius will return NULL
SELECT ST_DISTANCE_SPHERE(1, NULL, 3);
# Return ER_GIS_INVALID_DATA for invalid geometry
--error ER_GIS_INVALID_DATA
SELECT ST_DISTANCE_SPHERE(1, 1);
--error ER_GIS_INVALID_DATA
SELECT ST_DISTANCE_SPHERE(1, 1, 3);
# Return NULL if radius is NULL
SELECT ST_DISTANCE_SPHERE(1, 1, NULL);
# Wrong geometry
--error ER_INTERNAL_ERROR
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(1 0)'), ST_GEOMFROMTEXT('LINESTRING(0 0, 1 1)'));
--echo # Test Points and radius
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('POINT(1 1)'));
# make bb x86 happy
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(-1 -1)'), ST_GEOMFROMTEXT('POINT(-2 -2)')), 10);
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('POINT(1 1)'), 1);
--error ER_INTERNAL_ERROR
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('POINT(1 1)'), 0);
--error ER_INTERNAL_ERROR
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('POINT(1 1)'), -1);
--echo # Test longitude/lattitude
# make bb x86 happy
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 1)'), ST_GEOMFROMTEXT('POINT(1 2)')), 10);
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 1)'), ST_GEOMFROMTEXT('POINT(2 1)')), 10);
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(1 0)'), ST_GEOMFROMTEXT('POINT(1 2)')), 10);
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(1 0)'), ST_GEOMFROMTEXT('POINT(2 1)'));
--echo # Test Points - Multipoints
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(1 1)'));
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 1)'), ST_GEOMFROMTEXT('POINT(0 0)'));
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(1 1,2 2)'));
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(2 2,1 1)'));
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(1 1,2 2)'), 1);
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(2 2,1 1)'), 1);
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(2 2, 1 1, 3 4)'), 1);
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('POINT(0 0)'), ST_GEOMFROMTEXT('MULTIPOINT(2 2, 1 1,5 6)'), 1);
--echo # Test Multipoints - Multipoints
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(3 4,8 9 )'), ST_GEOMFROMTEXT('MULTIPOINT(3 4,8 9 )'));
# make bb x86 happy
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 2,1 1 )'), ST_GEOMFROMTEXT('MULTIPOINT(3 4,8 9 )')), 10);
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 2,1 1 )'), ST_GEOMFROMTEXT('MULTIPOINT(8 9,3 4 )')), 10);
# make bb x86 happy
SELECT TRUNCATE(ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 2,1 1 )'), ST_GEOMFROMTEXT('MULTIPOINT(8 9,3 4 )'),1), 17);
--error ER_INTERNAL_ERROR
SELECT ST_DISTANCE_SPHERE(ST_GEOMFROMTEXT('MULTIPOINT(1 2,1 1 )'), ST_GEOMFROMTEXT('MULTIPOINT(8 9,3 4 )'),0);
# Longitude out of range [-180,180]
set @pt1 = ST_GeomFromText('POINT(190 -30)');
set @pt2 = ST_GeomFromText('POINT(-30 50)');
--error ER_STD_OUT_OF_RANGE_ERROR
SELECT ST_Distance_Sphere(@pt1, @pt2);
# Latitude out of range [-90, 90]
set @pt1 = ST_GeomFromText('POINT(135 -30)');
set @pt2 = ST_GeomFromText('POINT(-30 91)');
--error ER_STD_OUT_OF_RANGE_ERROR
SELECT ST_Distance_Sphere(@pt1, @pt2);
# POINT in form (longitude[-180, 180] latitude[-90, 90])
set @zenica = ST_GeomFromText('POINT(17.907743 44.203438)');
set @sarajevo = ST_GeomFromText('POINT(18.413076 43.856258)');
SELECT TRUNCATE(ST_Distance_Sphere(@zenica, @sarajevo), 10);
SELECT TRUNCATE(ST_Distance_Sphere(@sarajevo, @zenica), 10);
......@@ -202,7 +202,6 @@ class Create_func_arg2 : public Create_func
@return An item representing the function call
*/
virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) = 0;
protected:
/** Constructor. */
Create_func_arg2() {}
......@@ -229,7 +228,6 @@ class Create_func_arg3 : public Create_func
@return An item representing the function call
*/
virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) = 0;
protected:
/** Constructor. */
Create_func_arg3() {}
......@@ -975,6 +973,19 @@ class Create_func_distance : public Create_func_arg2
Create_func_distance() {}
virtual ~Create_func_distance() {}
};
class Create_func_distance_sphere: public Create_native_func
{
public:
virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
static Create_func_distance_sphere s_singleton;
protected:
Create_func_distance_sphere() {}
virtual ~Create_func_distance_sphere() {}
};
#endif
......@@ -4761,6 +4772,26 @@ Create_func_glength::create_1_arg(THD *thd, Item *arg1)
{
return new (thd->mem_root) Item_func_glength(thd, arg1);
}
Create_func_distance_sphere Create_func_distance_sphere::s_singleton;
Item*
Create_func_distance_sphere::create_native(THD *thd, LEX_STRING name,
List<Item> *item_list)
{
int arg_count= 0;
if (item_list != NULL)
arg_count= item_list->elements;
if (arg_count < 2)
{
my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
return NULL;
}
return new (thd->mem_root) Item_func_sphere_distance(thd, *item_list);
}
#endif
......@@ -7051,6 +7082,7 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("ST_WITHIN") }, GEOM_BUILDER(Create_func_within)},
{ { C_STRING_WITH_LEN("ST_X") }, GEOM_BUILDER(Create_func_x)},
{ { C_STRING_WITH_LEN("ST_Y") }, GEOM_BUILDER(Create_func_y)},
{ { C_STRING_WITH_LEN("ST_DISTANCE_SPHERE") }, GEOM_BUILDER(Create_func_distance_sphere)},
{ { C_STRING_WITH_LEN("SUBSTRING_INDEX") }, BUILDER(Create_func_substr_index)},
{ { C_STRING_WITH_LEN("SUBTIME") }, BUILDER(Create_func_subtime)},
{ { C_STRING_WITH_LEN("TAN") }, BUILDER(Create_func_tan)},
......
......@@ -2537,11 +2537,151 @@ double Item_func_distance::val_real()
}
double Item_func_sphere_distance::val_real()
{
/* To test null_value of item, first get well-known bytes as a backups */
String bak1, bak2;
String *arg1= args[0]->val_str(&bak1);
String *arg2= args[1]->val_str(&bak2);
double distance= 0.0;
double sphere_radius= 6370986.0; // Default radius equals Earth radius
null_value= (args[0]->null_value || args[1]->null_value);
if (null_value)
{
goto handle_errors;
}
if (arg_count == 3)
{
sphere_radius= args[2]->val_real();
// Radius cannot be Null
if (args[2]->null_value)
{
null_value= true;
goto handle_errors;
}
if (sphere_radius <= 0)
{
my_error(ER_INTERNAL_ERROR, MYF(0), "Radius must be greater than zero.");
return 1;
}
}
Geometry_buffer buffer1, buffer2;
Geometry *g1, *g2;
if (!(g1= Geometry::construct(&buffer1, arg1->ptr(), arg1->length())) ||
!(g2= Geometry::construct(&buffer2, arg2->ptr(), arg2->length())))
{
my_error(ER_GIS_INVALID_DATA, MYF(0), "ST_Distance_Sphere");
goto handle_errors;
}
// Method allowed for points and multipoints
if (!(g1->get_class_info()->m_type_id == Geometry::wkb_point ||
g1->get_class_info()->m_type_id == Geometry::wkb_multipoint) ||
!(g2->get_class_info()->m_type_id == Geometry::wkb_point ||
g2->get_class_info()->m_type_id == Geometry::wkb_multipoint))
{
// Generate error message in case different geometry is used?
my_error(ER_INTERNAL_ERROR, MYF(0), func_name());
return 0;
}
distance= spherical_distance_points(g1, g2, sphere_radius);
if (distance < 0)
{
my_error(ER_INTERNAL_ERROR, MYF(0), "Returned distance cannot be negative.");
return 1;
}
return distance;
handle_errors:
return 0;
}
double Item_func_sphere_distance::spherical_distance_points(Geometry *g1,
Geometry *g2,
const double r)
{
double res= 0.0;
// Length for the single point (25 Bytes)
uint32 len= SRID_SIZE + POINT_DATA_SIZE + WKB_HEADER_SIZE;
int error= 0;
switch (g2->get_class_info()->m_type_id)
{
case Geometry::wkb_point:
// Optimization for point-point case
if (g1->get_class_info()->m_type_id == Geometry::wkb_point)
{
res= static_cast<Gis_point *>(g2)->calculate_haversine(g1, r, &error);
}
else
{
// Optimization for single point in Multipoint
if (g1->get_data_size() == len)
{
res= static_cast<Gis_point *>(g2)->calculate_haversine(g1, r, &error);
}
else
{
// There are multipoints in g1
// g1 is MultiPoint and calculate MP.sphericaldistance from g2 Point
if (g1->get_data_size() != GET_SIZE_ERROR)
static_cast<Gis_point *>(g2)->spherical_distance_multipoints(
(Gis_multi_point *)g1, r, &res, &error);
}
}
break;
case Geometry::wkb_multipoint:
// Optimization for point-point case
if (g1->get_class_info()->m_type_id == Geometry::wkb_point)
{
// Optimization for single point in Multipoint g2
if (g2->get_data_size() == len)
{
res= static_cast<Gis_point *>(g1)->calculate_haversine(g2, r, &error);
}
else
{
if (g2->get_data_size() != GET_SIZE_ERROR)
// g1 is a point (casted to multi_point) and g2 multipoint
static_cast<Gis_point *>(g1)->spherical_distance_multipoints(
(Gis_multi_point *)g2, r, &res, &error);
}
}
else
{
// Multipoints in g1 and g2 - no optimization
static_cast<Gis_multi_point *>(g1)->spherical_distance_multipoints(
(Gis_multi_point *)g2, r, &res, &error);
}
break;
default:
DBUG_ASSERT(0);
break;
}
if (res < 0)
goto handle_error;
handle_error:
if (error > 0)
my_error(ER_STD_OUT_OF_RANGE_ERROR, MYF(0),
"Longitude should be [-180,180]", "ST_Distance_Sphere");
else if(error < 0)
my_error(ER_STD_OUT_OF_RANGE_ERROR, MYF(0),
"Latitude should be [-90,90]", "ST_Distance_Sphere");
return res;
}
String *Item_func_pointonsurface::val_str(String *str)
{
Gcalc_operation_transporter trn(&func, &collector);
DBUG_ENTER("Item_func_pointonsurface::val_real");
DBUG_ENTER("Item_func_pointonsurface::val_str");
DBUG_ASSERT(fixed == 1);
String *res= args[0]->val_str(&tmp_value);
Geometry_buffer buffer;
......
......@@ -714,6 +714,20 @@ class Item_func_distance: public Item_real_func
};
class Item_func_sphere_distance: public Item_real_func
{
double spherical_distance_points(Geometry *g1, Geometry *g2,
const double sphere_r);
public:
Item_func_sphere_distance(THD *thd, List<Item> &list):
Item_real_func(thd, list) {}
double val_real();
const char *func_name() const { return "st_distance_sphere"; }
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
{ return get_item_copy<Item_func_sphere_distance>(thd, mem_root, this); }
};
class Item_func_pointonsurface: public Item_geometry_func
{
String tmp_value;
......
......@@ -1032,6 +1032,119 @@ const Geometry::Class_info *Gis_point::get_class_info() const
}
/**
Function to calculate haversine.
Taking as arguments Point and Multipoint geometries.
Multipoint geometry has to be single point only.
It is up to caller to ensure valid input.
@param g pointer to the Geometry
@param r sphere radius
@param error pointer describing the error in case of the boundary conditions
@return distance in case without error, it is caclulcated distance (non-negative),
in case error exist, negative value.
*/
double Gis_point::calculate_haversine(const Geometry *g,
const double sphere_radius,
int *error)
{
DBUG_ASSERT(sphere_radius > 0);
double x1r, x2r, y1r, y2r, dlong, dlat, res;
// This check is done only for optimization purposes where we know it will
// be one and only one point in Multipoint
if (g->get_class_info()->m_type_id == Geometry::wkb_multipoint)
{
const char point_size= 4 + WKB_HEADER_SIZE + POINT_DATA_SIZE+1; //1 for the type
char point_temp[point_size];
memset(point_temp+4, Geometry::wkb_point, 1);
memcpy(point_temp+5, static_cast<const Gis_multi_point *>(g)->get_data_ptr()+5, 4);
memcpy(point_temp+4+WKB_HEADER_SIZE, g->get_data_ptr()+4+WKB_HEADER_SIZE,
POINT_DATA_SIZE);
point_temp[point_size-1]= '\0';
Geometry_buffer gbuff;
Geometry *gg= Geometry::construct(&gbuff, point_temp, point_size-1);
DBUG_ASSERT(gg);
if (static_cast<Gis_point *>(gg)->get_xy_radian(&x2r, &y2r))
DBUG_ASSERT(0);
}
else
{
if (static_cast<const Gis_point *>(g)->get_xy_radian(&x2r, &y2r))
DBUG_ASSERT(0);
}
if (this->get_xy_radian(&x1r, &y1r))
DBUG_ASSERT(0);
// Check boundary conditions: longitude[-180,180]
if (!((x2r >= -M_PI && x2r <= M_PI) && (x1r >= -M_PI && x1r <= M_PI)))
{
*error=1;
return -1;
}
// Check boundary conditions: lattitude[-90,90]
if (!((y2r >= -M_PI/2 && y2r <= M_PI/2) && (y1r >= -M_PI/2 && y1r <= M_PI/2)))
{
*error=-1;
return -1;
}
dlat= sin((y2r - y1r)/2)*sin((y2r - y1r)/2);
dlong= sin((x2r - x1r)/2)*sin((x2r - x1r)/2);
res= 2*sphere_radius*asin((sqrt(dlat + cos(y1r)*cos(y2r)*dlong)));
return res;
}
/**
Function that calculate spherical distance of Point from Multipoint geometries.
In case there is single point in Multipoint geometries calculate_haversine()
can handle such case. Otherwise, new geometry (Point) has to be constructed.
@param g pointer to the Geometry
@param r sphere radius
@param result pointer to the result
@param err pointer to the error obtained from calculate_haversin()
@return state
@retval TRUE failed
@retval FALSE success
*/
int Gis_point::spherical_distance_multipoints(Geometry *g, const double r,
double *result, int *err)
{
uint32 num_of_points2;
// To find the minimum radius it cannot be greater than Earth radius
double res= 6370986.0;
double temp_res= 0.0;
const uint32 len= 4 + WKB_HEADER_SIZE + POINT_DATA_SIZE + 1;
char s[len];
g->num_geometries(&num_of_points2);
DBUG_ASSERT(num_of_points2 >= 1);
if (num_of_points2 == 1)
{
*result= this->calculate_haversine(g, r, err);
return 0;
}
for (uint32 i=1; i <= num_of_points2; i++)
{
Geometry_buffer buff_temp;
Geometry *temp;
// First 4 bytes are handled already, make sure to create a Point
memset(s + 4, Geometry::wkb_point, 1);
memcpy(s + 5, g->get_data_ptr() + 5, 4);
memcpy(s + 4 + WKB_HEADER_SIZE, g->get_data_ptr() + 4 + WKB_HEADER_SIZE*i +\
POINT_DATA_SIZE*(i-1), POINT_DATA_SIZE);
s[len-1]= '\0';
temp= Geometry::construct(&buff_temp, s, len);
DBUG_ASSERT(temp);
temp_res= this->calculate_haversine(temp, r, err);
if (res > temp_res)
res= temp_res;
}
*result= res;
return 0;
}
/***************************** LineString *******************************/
uint32 Gis_line_string::get_data_size() const
......@@ -2162,6 +2275,81 @@ const Geometry::Class_info *Gis_multi_point::get_class_info() const
}
/**
Function that calculate spherical distance of Multipoints geometries.
In case there is single point in Multipoint geometries calculate_haversine()
can handle such case. Otherwise, new geometry (Point) has to be constructed.
@param g pointer to the Geometry
@param r sphere radius
@param result pointer to the result
@param err pointer to the error obtained from calculate_haversin()
@return state
@retval TRUE failed
@retval FALSE success
*/
int Gis_multi_point::spherical_distance_multipoints(Geometry *g, const double r,
double *result, int *err)
{
const uint32 len= 4 + WKB_HEADER_SIZE + POINT_DATA_SIZE + 1;
// Check how many points are stored in Multipoints
uint32 num_of_points1, num_of_points2;
// To find the minimum radius it cannot be greater than Earth radius
double res= 6370986.0;
/* From Item_func_sphere_distance::spherical_distance_points,
we are sure that there will be multiple points and we have to construct
Point geometry and return the smallest result.
*/
num_geometries(&num_of_points1);
DBUG_ASSERT(num_of_points1 >= 1);
g->num_geometries(&num_of_points2);
DBUG_ASSERT(num_of_points2 >= 1);
for (uint32 i=1; i <= num_of_points1; i++)
{
Geometry_buffer buff_temp;
Geometry *temp;
double temp_res= 0.0;
char s[len];
// First 4 bytes are handled already, make sure to create a Point
memset(s + 4, Geometry::wkb_point, 1);
memcpy(s + 5, this->get_data_ptr() + 5, 4);
memcpy(s + 4 + WKB_HEADER_SIZE, this->get_data_ptr() + 4 + WKB_HEADER_SIZE*i +\
POINT_DATA_SIZE*(i-1), POINT_DATA_SIZE);
s[len-1]= '\0';
temp= Geometry::construct(&buff_temp, s, len);
DBUG_ASSERT(temp);
// Optimization for single Multipoint
if (num_of_points2 == 1)
{
*result= static_cast<Gis_point *>(temp)->calculate_haversine(g, r, err);
return 0;
}
for (uint32 j=1; j<= num_of_points2; j++)
{
Geometry_buffer buff_temp2;
Geometry *temp2;
char s2[len];
// First 4 bytes are handled already, make sure to create a Point
memset(s2 + 4, Geometry::wkb_point, 1);
memcpy(s2 + 5, g->get_data_ptr() + 5, 4);
memcpy(s2 + 4 + WKB_HEADER_SIZE, g->get_data_ptr() + 4 + WKB_HEADER_SIZE*j +\
POINT_DATA_SIZE*(j-1), POINT_DATA_SIZE);
s2[len-1]= '\0';
temp2= Geometry::construct(&buff_temp2, s2, len);
DBUG_ASSERT(temp2);
temp_res= static_cast<Gis_point *>(temp)->calculate_haversine(temp2, r, err);
if (res > temp_res)
res= temp_res;
}
}
*result= res;
return 0;
}
/***************************** MultiLineString *******************************/
uint32 Gis_multi_line_string::get_data_size() const
......
......@@ -332,6 +332,11 @@ class Geometry
m_data+= WKB_HEADER_SIZE;
}
const char *get_data_ptr() const
{
return m_data;
}
bool envelope(String *result) const;
static Class_info *ci_collection[wkb_last+1];
......@@ -410,6 +415,17 @@ class Gis_point: public Geometry
return 0;
}
int get_xy_radian(double *x, double *y) const
{
if (!get_xy(x, y))
{
*x= (*x)*M_PI/180;
*y= (*y)*M_PI/180;
return 0;
}
return 1;
}
int get_x(double *x) const
{
if (no_data(m_data, SIZEOF_STORED_DOUBLE))
......@@ -436,6 +452,10 @@ class Gis_point: public Geometry
}
int store_shapes(Gcalc_shape_transporter *trn) const;
const Class_info *get_class_info() const;
double calculate_haversine(const Geometry *g, const double sphere_radius,
int *error);
int spherical_distance_multipoints(Geometry *g, const double r, double *result,
int *error);
};
......@@ -535,6 +555,8 @@ class Gis_multi_point: public Geometry
}
int store_shapes(Gcalc_shape_transporter *trn) const;
const Class_info *get_class_info() const;
int spherical_distance_multipoints(Geometry *g, const double r, double *res,
int *error);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment