MySQL convert Degree, Minutes, Seconds to Degree decimal - mysql

I have multiple rows of Degrees Minutes Seconds that I need to convert with a query.
36°19'11.46" N = 36.31985
95°36'02.22" W = 95.600617
Each row is going to be different. I've been stuck on this for two days. Is this even possible?

Nice lifehack: reverse problem solution (degree to DMS) using SEC_TO_TIME built-in MySQL function:
CREATE FUNCTION `geocoords`(lon double, lat double) RETURNS varchar(24) CHARSET cp1251
NO SQL
DETERMINISTIC
begin
declare alon double;
declare alat double;
declare slon varchar(12);
declare slat varchar(12);
set alon = abs(lon);
set alat = abs(lat);
set slon = TIME_FORMAT(SEC_TO_TIME(alon*3600), '%H°%i''%s"');
set slat = TIME_FORMAT(SEC_TO_TIME(alat*3600), '%H°%i''%s"');
if lon>0 then
set slon = concat(slon, 'E');
elseif lon<0 then
set slon = concat(slon, 'W');
end if;
if lat>0 then
set slat = concat(slat, 'N');
elseif lat<0 then
set slat = concat(slat, 'S');
end if;
return concat(slat, ' ', slon);
end
SELECT geocoords(30.550157546997, 50.344024658203)
50°20'38"N 30°33'01"E

The following should work:
SELECT D + M/60 + S/3600;
For example, in MySQL:
SELECT 36 + 19/60 + 11.46/3600;
returns: 36.319850

I ended up building this, and it worked flawlessly with what I needed. You will note that I added a C to the numbers, this is to flag them, so if it had already been converted it wouldn't continue to convert.
UPDATE
MasterTable
SET
MasterTable.Latitude_A = MasterTable.Latitude,
MasterTable.Longitude_A = MasterTable.Longitude
WHERE
ProjectID = 'ProjectAlpha'
and Sequence = '0'
and MasterTable.Latitude NOT LIKE '%C%'
and MasterTable.Longitude NOT LIKE '%C%';
TRUNCATE TABLE gpsconvert;
INSERT into gpsconvert(gpsconvert.`Account Number`,gpsconvert.Latitude,gpsconvert.Longitude)
SELECT
MasterTable.AccountNumber,
MasterTable.Latitude,
MasterTable.Longitude
FROM
MasterTable
WHERE
MasterTable.ProjectID = 'ProjectAlpha'
and MasterTable.Sequence = '0'
and MasterTable.Latitude NOT LIKE '%c%'
and MasterTable.Longitude NOT LIKE '%c%'
and MasterTable.Latitude <> ''
and MasterTable.Longitude <> '';
UPDATE
gpsconvert
SET
gpsconvert.LatDegree = LEFT(gpsconvert.Latitude,2),
gpsconvert.LatMinutes = SUBSTRING(gpsconvert.Latitude,-7,2),
gpsconvert.LatSeconds = SUBSTRING(gpsconvert.latitude,-5,5),
gpsconvert.LatDecimal = gpsconvert.LatDegree + (gpsconvert.LatMinutes/60) + (gpsconvert.LatSeconds/3600),
gpsconvert.LongDegree = LEFT(gpsconvert.Longitude,2),
gpsconvert.LongMinutes = SUBSTRING(gpsconvert.Longitude,-7,2),
gpsconvert.LongSeconds = SUBSTRING(gpsconvert.Longitude,-5,5),
gpsconvert.LongDecimal = gpsconvert.LongDegree + (gpsconvert.LongMinutes/60) + (gpsconvert.LongSeconds/3600);
UPDATE
MasterTable
INNER JOIN
gpsconvert on gpsconvert.`Account Number` = MasterTable.AccountNumber
SET
MasterTable.Latitude = CONCAT(gpsconvert.LatDecimal,'c'),
MasterTable.Longitude = CONCAT(gpsconvert.LongDecimal,'c')
WHERE
MasterTable.ProjectID = 'ProjectAlpha'
and MasterTable.Sequence = '0'
and MasterTable.AccountNumber = gpsconvert.`Account Number`

This converts strings in the form 21°48'6.384" to decimal
CREATE FUNCTION database.DMS_TO_DECIMAL(dms varchar(20))
RETURNS decimal(12,9)
BEGIN
DECLARE deg decimal(12,9);
DECLARE mins decimal(12,9);
DECLARE secs decimal(12,9);
SET deg = CAST( SUBSTRING_INDEX(dms, '°', 1) AS decimal(12,9));
SET mins = CAST( (SUBSTR(dms, POSITION('°' IN dms) + 1, POSITION("'" IN dms) - POSITION('°' IN dms) - 1)) AS decimal(12,9));
SET secs = CAST( (SUBSTR(dms, POSITION("'" IN dms) + 1, POSITION("\"" IN dms) - POSITION("'" IN dms) - 1)) AS decimal(12,9));
RETURN deg + mins/60 + secs/3600;
END

Add orientation (W/S):
DROP FUNCTION IF EXISTS `DmsToDecimal`;
DELIMITER $$
CREATE FUNCTION `DmsToDecimal`(`dms` VARCHAR(50))
RETURNS DECIMAL(12,9)
DETERMINISTIC
NO SQL
SQL SECURITY INVOKER
BEGIN
DECLARE deg decimal(12,9);
DECLARE mins decimal(12,9);
DECLARE secs decimal(12,9);
DECLARE sign integer;
SET dms = UPPER(TRIM(REPLACE(dms, ",", ".")));
SET deg = CAST( SUBSTRING_INDEX(dms, '°', 1) AS decimal(12,9));
SET mins = CAST( (SUBSTR(dms, POSITION('°' IN dms) + 1, POSITION("'" IN dms) - POSITION("°" IN dms) - 1)) AS decimal(12,9));
SET secs = CAST( (SUBSTR(dms, POSITION("'" IN dms) + 1, POSITION("""" IN dms) - POSITION("'" IN dms) - 1)) AS decimal(12,9));
SET sign = 1 - 2 * (RIGHT(dms, 1) = "W" OR RIGHT(dms, 1) = "S";
RETURN sign * (deg + mins / 60 + secs / 3600);
END$$
DELIMITER ;

Related

Creating funcion for short date in spanish

Hi guys this function gives me this error: "Something wrong in line 35." (RETURN diaCompleto;), I dont know whats wrong with the code below anyway can point me out in the correct direction, tyvm.
DELIMITER $$
CREATE FUNCTION DIA_FECHA(fecha date)
RETURNS VARCHAR(8)
BEGIN
DECLARE nomDia VARCHAR(6);
DECLARE dia VARCHAR(2);
DECLARE diaCompleto VARCHAR(8);
SET dia = LPAD(DAY(fecha),2,'0');
IF(DAYOFWEEK(fecha) = 1) THEN
SET nomDia = 'DOM - ';
IF(DAYOFWEEK(fecha) = 2) THEN
SET nomDia = 'LUN - ';
IF(DAYOFWEEK(fecha) = 3) THEN
SET nomDia = 'MAR - ';
IF(DAYOFWEEK(fecha) = 4) THEN
SET nomDia = 'MIE - ';
IF(DAYOFWEEK(fecha) = 5) THEN
SET nomDia = 'JUE - ';
IF(DAYOFWEEK(fecha) = 6) THEN
SET nomDia = 'VIE - ';
IF(DAYOFWEEK(fecha) = 7) THEN
SET nomDia = 'SAB - ';
SET diaCompleto = CONCAT(nomDia,dia);
RETURN diaCompleto;
END$$
DELIMITER ;
The reason for your syntax error is that you need to use END IF; after each IF/THEN. See examples in the manual: https://dev.mysql.com/doc/refman/8.0/en/if.html
I think you don't need to write this function at all. Instead, you could use MySQL's DATE_FORMAT() function. It supports locale-aware names for days of the week.
mysql> set lc_time_names = 'es_ES';
mysql> select date_format(curdate(), '%a - %d') as diaCompleto;
+-------------+
| diaCompleto |
+-------------+
| sáb - 19 |
+-------------+

"object reference not set to the instance of an object" when i run the query- mssql

Please access the following link.
enter image description here
I would like to calculate the values for each column ( Jul2015 , Aug2015. ) The formula is listed as follows .
MMMYYYY = bookrate * number of days for each activity
e.g row 1 : July2015 = 18937 *23 (from 1/7 - 23/7) ,
row 2: July2015 =20503*9 (from 23/7-31/7) , Aug2015 =20503*20 (1/8-20/8)
*I wrote the following code to try to cater the first row.But it turned out an error message "Object reference not set to an instance of an object" ? May anyone tell me why this message will appear
select * into #tempcost1 from tempcost
Declare #i int
Set #i =1
Declare #numofrow int
Set #numofrow = (Select COunt(*) FROM #tempcost1)
Declare #startmonth datetime
Declare #monthdiff int
Declare #update1 varchar(max)
Declare #month1 varchar(20)
Declare #activitysate datetime
While (#i<=#numofrow)
Begin
Set #monthdiff= (Select diff_month FROM #tempcost1 where RowNumber= #i)
Set #activitysate = (Select activity_startdate FROM #tempcost1 where RowNumber= #i)
Set #month1= SUBSTRING (convert(varchar, #activitysate, 100),1,3) +
SUBSTRING (convert(varchar, #activitysate, 100),8,4 )
if (#monthdiff = 1)
Begin
Set #update1 = 'Update #tempcost1 set ' + CAST(#month1 as varchar) + ' =(Select bookrate from #tempcost1 where Rownumber=' + cast(#i as varchar) +') ' + 'where RowNumber=' + cast(#i as varchar)
exec(#update1)
End
Set #i = #i +1
End

Issue while execute tsql function

I have written the following function:
CREATE FUNCTION dbo.GetData
(#value AS VARCHAR(MAX),
#pattern AS VARCHAR(MAX),
#masker AS VARCHAR,
#notMaskedCount AS INT)
RETURNS NCHAR(47)
AS BEGIN
DECLARE #nextPatIdx INT
SET #nextPatIdx = PATINDEX('%' + #pattern + '%', #value)
WHILE #nextPatIdx > 0 AND #nextPatIdx < LEN(#value) - #notMaskedCount
BEGIN
SET #value = Stuff(#value, #nextPatIdx, 1, #masker)
SET #nextPatIdx = PATINDEX('%' + #pattern + '%', #value)
END
RETURN CONVERT(NCHAR(40), #value) + '_data'
END
Run it with :
select dbo.GetData('152648494','[a-zA-Z0-9]', 'x', 4)
If I execute it throw C# and try readdata it throws exception
If I runt it viahttp://sqlfiddle.com/#!6/6ee81/1 I get an error
It doen`t complete the execution
What is wrong - the function / the way I call it?
You achieved a infinite loop as you can check using this piece of code:
---select dbo.GetData('152648494','[a-zA-Z0-9]', 'x', 4)
declare
#value AS VARCHAR(MAX) = '152648494',
#pattern AS VARCHAR(MAX) = '[a-zA-Z0-9]',
#masker AS VARCHAR(1) = 'x',
#notMaskedCount AS INT = 4
DECLARE #nextPatIdx INT
SET #nextPatIdx = PATINDEX('%' + #pattern + '%', #value)
WHILE #nextPatIdx > 0 AND #nextPatIdx < LEN(#value) - #notMaskedCount
BEGIN
SET #value = Stuff(#value, #nextPatIdx, 1, #masker)
SET #nextPatIdx = PATINDEX('%' + #pattern + '%', #value)
print #nextPatIdx;
END
print 'END'
select CONVERT(NCHAR(40), #value) + '_data'
Maybe if you change the inequality
#nextPatIdx > 0
to
#nextPatIdx > 1
It ill exit the infinite loop but I need more info to find out what you desired output
EDIT
I guess you need to change your loop to it.
WHILE #nextPatIdx > 0 AND #nextPatIdx <= LEN(#value) - #notMaskedCount
BEGIN
SET #value = Stuff(#value, #nextPatIdx, 1, #masker)
--SET #nextPatIdx = PATINDEX('%' + #pattern + '%', #value)
SET #nextPatIdx += 1
END

Converting MSSQL function to MySQL function

I am trying to convert this user defined function (taken from a MSSQL) and tweak it so that I can run it MYSQL. I have made several attempts but all seem to error on the declare variable.
I am running the following version: 5.6.11-log - MySQL Community Server (GPL)
USE [DataGB]
GO
/****** Object: UserDefinedFunction [dbo].[FullPostCodeFix] Script Date: 11/20/2013 16:10:44 ******/
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
CREATE FUNCTION [dbo].[FullPostCodeFix] (#Postcode VARCHAR(20))
RETURNS VARCHAR(20)
BEGIN
/*
Puts postcode into correct format if it is currently in any of the below formats
AB12AA
AB 12AA
AB1 2AA
AB 1 2AA
Returns #Postcode
*/
DECL ARE #District Varchar(50)
DECLARE #Remainder Varchar(50)
DECLARE #Sector Varchar(50)
SET #District= CASE
WHEN LEN(#Postcode) - CHARINDEX(' ', REVERSE(#Postcode)) = len(#Postcode) THEN SUBSTRING(#Postcode,1,(len(#Postcode) - 3))
WHEN LEN(#Postcode) - CHARINDEX(' ', REVERSE(#Postcode)) < 3 THEN SUBSTRING(#Postcode,1,(len(#Postcode) - 3))
ELSE SUBSTRING(#Postcode, 0, LEN(#Postcode) - CHARINDEX(' ', REVERSE(#Postcode)) + 1)
END
SET #District = dbo.PostcodeFix(#District)
SET #Remainder= RIGHT(#Postcode,3)
SET #Sector = #District + ' ' + LEFT(#Remainder,1)
SET #Postcode = #District + ' ' + #Remainder
RETURN #Postcode
END
My attempt at creating a MYSQL version is below:
CREATE FUNCTION FullPostCodeFix (Postcode VARCHAR(20))
RETURNS VARCHAR(20)
BEGIN
DECLARE District Varchar(50)
DECLARE Remainder Varchar(50)
DECLARE Sector Varchar(50)
SET District= CASE
WHEN LEN(Postcode) - CHARINDEX(' ', REVERSE(Postcode)) = len(Postcode) THEN SUBSTRING(Postcode,1,(len(Postcode) - 3))
WHEN LEN(Postcode) - CHARINDEX(' ', REVERSE(Postcode)) < 3 THEN SUBSTRING(Postcode,1,(len(Postcode) - 3))
ELSE SUBSTRING(Postcode, 0, LEN(Postcode) - CHARINDEX(' ', REVERSE(Postcode)) + 1)
END
SET District = dbo.PostcodeFix(District)
SET Remainder= RIGHT(Postcode,3)
SET Sector = District + ' ' + LEFT(Remainder,1)
SET Postcode = District + ' ' + Remainder
RETURN Postcode
END
The error that I get is as follows:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE Remainder Varchar(50) DECLARE Sector Varchar(50)
There is another function called from within the function "FullPostCodeFix". This is my attempt:
DELIMITER $$
CREATE FUNCTION PostCodeFix (strDistrict VARCHAR(20))
RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
DECLARE intASCII INTEGER;
SET strDistrict = LTRIM(strDistrict);
SET strDistrict = RTRIM(strDistrict);
IF LENGTH(strDistrict) > 4 OR LENGTH(strDistrict) = 0 THEN RETURN 'ERROR: ' + strDistrict;
ELSE
BEGIN
SET intASCII = ASCII(LEFT(strDistrict, 1));
IF ( intASCII > 47 AND intASCII < 58 ) THEN RETURN 'ERROR: ' + strDistrict;
ELSE
BEGIN
SET intASCII = ASCII(SUBSTRING(strDistrict, 2, 1));
IF ( intASCII > 47 AND intASCII < 58 ) THEN SET strDistrict = LEFT(strDistrict, 1) + ' ' + RIGHT(strDistrict, LENGTH(strDistrict)-1);
SET intASCII = ASCII(SUBSTRING(strDistrict, 3, 1));
IF ( intASCII < 48 OR intASCII > 57 ) AND (intASCII <> 32) THEN RETURN 'ERROR: ' + strDistrict;
ELSE IF LENGTH(strDistrict) < 4 THEN SET strDistrict = LEFT(strDistrict, 2) + ' ' + RIGHT(strDistrict, LENGTH(strDistrict)-2);
END IF;
END IF;
RETURN strDistrict;
END IF;
Ok lets start, replace MS-SQL functions with MySQL equivalent functions.
MSSQL MySQL
LEN() LENGTH()
SUBTRING() SUBSTR()
CHARINDEX() INSTR()
below is documentation and list of all MySQL String functions
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html
Here is correct MySQL Syntax that I have verified.
CREATE FUNCTION FullPostCodeFix (Postcode VARCHAR(20)) RETURNS VARCHAR(20)
BEGIN
DECLARE district VARCHAR(50);
DECLARE remainder VARCHAR(50);
DECLARE sector VARCHAR(50);
IF LENGTH(Postcode) - INSTR(' ', REVERSE(Postcode)) = LENGTH(Postcode) THEN SET district = SUBSTR(Postcode,1,(LENGTH(Postcode) - 3));
ELSEIF LENGTH(Postcode) - INSTR(' ', REVERSE(Postcode)) < 3 THEN SET district = SUBSTR(Postcode,1,(LENGTH(Postcode) - 3));
ELSE SET district = SUBSTR(Postcode, 0, LENGTH(Postcode) - INSTR(' ', REVERSE(Postcode)) + 1);
END IF;
SET District = dbo.PostcodeFix(District);
SET Remainder= RIGHT(Postcode,3);
SET Sector = CONCAT(District,' ',LEFT(Remainder,1));
SET Postcode = CONCAT(District,' ',Remainder);
RETURN Postcode;
END
Here is your second function
CREATE FUNCTION PostCodeFix (strDistrict VARCHAR(20))
RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
DECLARE intASCII INTEGER;
SET strDistrict = LTRIM(strDistrict);
SET strDistrict = RTRIM(strDistrict);
IF LENGTH(strDistrict) > 4 OR LENGTH(strDistrict) = 0 THEN RETURN 'ERROR: ' + strDistrict;
ELSE
SET intASCII = ASCII(LEFT(strDistrict, 1));
IF ( intASCII > 47 AND intASCII < 58 ) THEN RETURN 'ERROR: ' + strDistrict;
ELSE
SET intASCII = ASCII(SUBSTRING(strDistrict, 2, 1));
IF ( intASCII > 47 AND intASCII < 58 ) THEN SET strDistrict = LEFT(strDistrict, 1) + ' ' + RIGHT(strDistrict, LENGTH(strDistrict)-1);
END IF;
SET intASCII = ASCII(SUBSTRING(strDistrict, 3, 1));
IF ( intASCII < 48 OR intASCII > 57 ) AND (intASCII <> 32) THEN RETURN 'ERROR: ' + strDistrict;
ELSEIF LENGTH(strDistrict) < 4 THEN SET strDistrict = LEFT(strDistrict, 2) + ' ' + RIGHT(strDistrict, LENGTH(strDistrict)-2);
END IF;
END IF;
RETURN strDistrict;
END IF;
END

MySQL implementation of ray-casting Algorithm?

We need to figure out a quick and fairly accurate method for point-in-polygon for lat/long values and polygons over google maps. After some research - came across some posts about mysql geometric extensions, and did implement that too -
SELECT id, Contains( PolyFromText( 'POLYGON(".$polygonpath.")' ) , PointFromText( concat( \"POINT(\", latitude, \" \", longitude, \")\" ) ) ) AS
CONTAINS
FROM tbl_points
That did not however work with polygons made up of a large number of points :(
After some more research - came across a standard algorithm called the Ray-casting algorithm but before trying developing a query for that in MySQL, wanted to take my chances if someone had already been through that or came across a useful link which shows how to implement the algorithm in MySQL / SQL-server.
So, cutting it short - question is:
Can anyone please provide the MySQL/SQL-server implementation of Ray casting algorithm?
Additional detail:
Polygons are either of concave, convex or complex.
Targeting quick execution over 100% accuracy.
The following function (MYSQL version of Raycasting algorithm) rocked my world :
CREATE FUNCTION myWithin(p POINT, poly POLYGON) RETURNS INT(1) DETERMINISTIC
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE pX DECIMAL(9,6);
DECLARE pY DECIMAL(9,6);
DECLARE ls LINESTRING;
DECLARE poly1 POINT;
DECLARE poly1X DECIMAL(9,6);
DECLARE poly1Y DECIMAL(9,6);
DECLARE poly2 POINT;
DECLARE poly2X DECIMAL(9,6);
DECLARE poly2Y DECIMAL(9,6);
DECLARE i INT DEFAULT 0;
DECLARE result INT(1) DEFAULT 0;
SET pX = X(p);
SET pY = Y(p);
SET ls = ExteriorRing(poly);
SET poly2 = EndPoint(ls);
SET poly2X = X(poly2);
SET poly2Y = Y(poly2);
SET n = NumPoints(ls);
WHILE i<n DO
SET poly1 = PointN(ls, (i+1));
SET poly1X = X(poly1);
SET poly1Y = Y(poly1);
IF ( ( ( ( poly1X <= pX ) && ( pX < poly2X ) ) || ( ( poly2X <= pX ) && ( pX < poly1X ) ) ) && ( pY > ( poly2Y - poly1Y ) * ( pX - poly1X ) / ( poly2X - poly1X ) + poly1Y ) ) THEN
SET result = !result;
END IF;
SET poly2X = poly1X;
SET poly2Y = poly1Y;
SET i = i + 1;
END WHILE;
RETURN result;
End;
Add
DELIMITER ;;
before the function as required.
The usage for the function is:
SELECT myWithin(point, polygon) as result;
where
point = Point(lat,lng)
polygon = Polygon(lat1 lng1, lat2 lng2, lat3 lng3, .... latn lngn, lat1 lng1)
Please note that the polygon ought to be closed (normally it is closed if you're retrieving a standard kml or googlemap data but just make sure it is - note lat1 lng1 set is repeated at the end)
I did not have points and polygons in my database as geometric fields, so I had to do something like:
Select myWithin(PointFromText( concat( "POINT(", latitude, " ", longitude, ")" ) ),PolyFromText( 'POLYGON((lat1 lng1, ..... latn lngn, lat1 lng1))' ) ) as result
I hope this might help someone.
I would write a custom UDF that implements the ray-casting algorithm in C or Delphi or whatever high level tool you use:
Links for writing a UDF
Here's sourcecode for a MySQL gis implementation that looks up point on a sphere (use it as a template to see how to interact with the spatial datatypes in MySQL).
http://www.lenzg.net/archives/220-New-UDF-for-MySQL-5.1-provides-GIS-functions-distance_sphere-and-distance_spheroid.html
From the MySQL manual:
http://dev.mysql.com/doc/refman/5.0/en/adding-functions.html
UDF tutorial for MS Visual C++
http://rpbouman.blogspot.com/2007/09/creating-mysql-udfs-with-microsoft.html
UDF tutorial in Delphi:
Creating a UDF for MySQL in Delphi
Source-code regarding the ray-casting algorithm
Pseudo-code: http://rosettacode.org/wiki/Ray-casting_algorithm
Article in drDobbs (note the link to code at the top of the article): http://drdobbs.com/cpp/184409586
Delphi (actually FreePascal): http://www.cabiatl.com/mricro/raycast/
Just in case, one MySQL function which accepts MULTIPOLYGON as an input: http://forums.mysql.com/read.php?23,286574,286574
DELIMITER $$
CREATE DEFINER=`root`#`localhost` FUNCTION `GISWithin`(pt POINT, mp MULTIPOLYGON) RETURNS int(1)
DETERMINISTIC
BEGIN
DECLARE str, xy TEXT;
DECLARE x, y, p1x, p1y, p2x, p2y, m, xinters DECIMAL(16, 13) DEFAULT 0;
DECLARE counter INT DEFAULT 0;
DECLARE p, pb, pe INT DEFAULT 0;
SELECT MBRWithin(pt, mp) INTO p;
IF p != 1 OR ISNULL(p) THEN
RETURN p;
END IF;
SELECT X(pt), Y(pt), ASTEXT(mp) INTO x, y, str;
SET str = REPLACE(str, 'POLYGON((','');
SET str = REPLACE(str, '))', '');
SET str = CONCAT(str, ',');
SET pb = 1;
SET pe = LOCATE(',', str);
SET xy = SUBSTRING(str, pb, pe - pb);
SET p = INSTR(xy, ' ');
SET p1x = SUBSTRING(xy, 1, p - 1);
SET p1y = SUBSTRING(xy, p + 1);
SET str = CONCAT(str, xy, ',');
WHILE pe > 0 DO
SET xy = SUBSTRING(str, pb, pe - pb);
SET p = INSTR(xy, ' ');
SET p2x = SUBSTRING(xy, 1, p - 1);
SET p2y = SUBSTRING(xy, p + 1);
IF p1y < p2y THEN SET m = p1y; ELSE SET m = p2y; END IF;
IF y > m THEN
IF p1y > p2y THEN SET m = p1y; ELSE SET m = p2y; END IF;
IF y <= m THEN
IF p1x > p2x THEN SET m = p1x; ELSE SET m = p2x; END IF;
IF x <= m THEN
IF p1y != p2y THEN
SET xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x;
END IF;
IF p1x = p2x OR x <= xinters THEN
SET counter = counter + 1;
END IF;
END IF;
END IF;
END IF;
SET p1x = p2x;
SET p1y = p2y;
SET pb = pe + 1;
SET pe = LOCATE(',', str, pb);
END WHILE;
RETURN counter % 2;
END
In reply to zarun function for finding lat/long within polygon.
I had a property table having lat/long information. So I had to get the records whose lat/long lies within polygon lats/longs (which I got from Google API). At first I was dumb how to use the Zarun function. So here is the solution query for it.
Table: properties
Fields: id, latitude, longitude, beds etc...
Query:
SELECT id
FROM properties
WHERE myWithin(
PointFromText(concat( "POINT(", latitude, " ", longitude, ")")),
PolyFromText('POLYGON((37.628134 -77.458334,37.629867 -77.449021,37.62324 -77.445416,37.622424 -77.457819,37.628134 -77.458334))' )
) = 1 limit 0,50;
Hope it will save time for dumbs like me ;)
I wanted to use the above mywithin stored procedure on a table of polygons so here are the commands to do just that.
After importing a shapefile containing polygons into mysql using ogr2ogr as follows
ogr2ogr -f "mysql" MYSQL:"mydbname,host=localhost,user=root,password=mypassword,port=3306" -nln "mytablename" -a_srs "EPSG:4326" /path/to/shapefile.shp
you can then use MBRwithin to prefilter your table and mywithin to finish as follows
DROP TEMPORARY TABLE IF EXISTS POSSIBLE_POLYS;
CREATE TEMPORARY TABLE POSSIBLE_POLYS(OGR_FID INT,SHAPE POLYGON);
INSERT INTO POSSIBLE_POLYS (OGR_FID, SHAPE) SELECT mytablename.OGR_FID,mytablename.SHAPE FROM mytablename WHERE MBRwithin(#testpoint,mytablename.SHAPE);
DROP TEMPORARY TABLE IF EXISTS DEFINITE_POLY;
CREATE TEMPORARY TABLE DEFINITE_POLY(OGR_FID INT,SHAPE POLYGON);
INSERT INTO DEFINITE_POLY (OGR_FID, SHAPE) SELECT POSSIBLE_POLYS.OGR_FID,POSSIBLE_POLYS.SHAPE FROM POSSIBLE_POLYS WHERE mywithin(#testpoint,POSSIBLE_POLYS.SHAPE);
where #testpoint is created, for example, from
SET #longitude=120;
SET #latitude=-30;
SET #testpoint =(PointFromText( concat( "POINT(", #longitude, " ", #latitude, ")" ) ));
It is now a Spatial Extension as of MySQL5.6.1 and above. See function_st-contains at Docs.
Here is a version that works with MULTIPOLYGONS (an adaptation of Zarun's one which only works for POLYGONS).
CREATE FUNCTION GISWithin(p POINT, multipoly MULTIPOLYGON) RETURNS INT(1) DETERMINISTIC
BEGIN
DECLARE n,i,m,x INT DEFAULT 0;
DECLARE pX,pY,poly1X,poly1Y,poly2X,poly2Y DECIMAL(13,10);
DECLARE ls LINESTRING;
DECLARE poly MULTIPOLYGON;
DECLARE poly1,poly2 POINT;
DECLARE result INT(1) DEFAULT 0;
SET pX = X(p);
SET pY = Y(p);
SET m = NumGeometries(multipoly);
WHILE x<m DO
SET poly = GeometryN(multipoly,x);
SET ls = ExteriorRing(poly);
SET poly2 = EndPoint(ls);
SET poly2X = X(poly2);
SET poly2Y = Y(poly2);
SET n = NumPoints(ls);
WHILE i<n DO
SET poly1 = PointN(ls, (i+1));
SET poly1X = X(poly1);
SET poly1Y = Y(poly1);
IF ( ( ( ( poly1X <= pX ) && ( pX < poly2X ) ) || ( ( poly2X <= pX ) && ( pX < poly1X ) ) ) && ( pY > ( poly2Y - poly1Y ) * ( pX - poly1X ) / ( poly2X - poly1X ) + poly1Y ) ) THEN
SET result = !result;
END IF;
SET poly2X = poly1X;
SET poly2Y = poly1Y;
SET i = i + 1;
END WHILE;
SET x = x + 1;
END WHILE;
RETURN result;
End;