Mysql Spatial Extension. How to check if Linestring CONTAINS a Point? - mysql

I'm trying to determine if Linestring have Point.... f.e.
SET ls = geomfromtext('LINESTRING(1 0,3 0)');
SET p = geomfromtext('POINT(2 0)');
if i do CONTAINS(ls,p) i have true. but there is no point (2 0) in line
i need exactly containing. is there any function for it?

i tried all functions from documentation and non of them do what i need.
f.e.
SELECT ASTEXT(path) FROM paths WHERE INTERSECTS(path, GEOMFROMTEXT('POINT(604 0)'))
gives "wrong" results
LINESTRING(572 0,600 0,601 0,602 0,603 0,604 0,605 0,606 0,607 0,608 0,402 0)
LINESTRING(402 0,609 0,610 0,611 0,612 0,613 0,614 0,615 0,616 0,617 0,618 0,619 0,620 0,621 0,622 0,623 0)
LINESTRING(359 0,449 0,801 0,422 0,802 0,803 0,498 0)
LINESTRING(572 0,795 0,796 0,797 0,798 0,799 0,800 0,345 0,359 0)
LINESTRING(792 0,768 0,793 0,794 0,572 0)
LINESTRING(342 0,904 0,905 0,906 0)
LINESTRING(912 0,914 0,915 0,916 0,341 0)
LINESTRING(344 0,917 0,918 0,919 0,920 0,800 0)
LINESTRING(918 0,922 0,923 0,924 0,925 0,926 0,927 0,343 0)
LINESTRING(940 0,947 0,948 0,949 0,604 0)
MBRWITHIN gives the same result
i wrote a function, but it is very-very slow:
FUNCTION `IDIL`(`id` INT, `line` LINESTRING) RETURNS INT(1)
NO SQL
DETERMINISTIC
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE p1X INT(20);
DECLARE p1Y INT(20);
DECLARE p1 POINT;
DECLARE i INT DEFAULT 0;
DECLARE result INT(1) DEFAULT 0;
SET n = NUMPOINTS(line);
WHILE i<n DO
SET p1 = POINTN(line, (i+1));
SET p1X = X(p1);
SET p1Y = Y(p1);
IF p1X=id OR p1Y=id THEN RETURN 1; END IF;
SET i = i + 1;
END WHILE;
RETURN result;
END$$

Could you please check out the following reference on this article
SQLFIDDLE
SET #ls = 'LineString(1 0,3 0)';
SET #xs = geomfromtext(#ls);
SET #p = geomfromtext('POINT(2 0)');
SELECT MBRWithin(#xs,#p);
Sorry I have given the wrong link.
results
MBRWITHIN(#XS,#P)
0
Looking at your line 1 0, 3 0 --> 2 0 point exists on it.

Related

Convert MySQL functions POSITION and SUBSTR from JavaScript functions indexOf and charAt

I'd like to transform a JavaScript function to a MySQL one.
The JavaScript code is like:
var set1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var set2 = "ABCDEFGHIJABCDEFGHIJKLMNOPQRSTUVWXYZ";
var setpari = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var setdisp = "BAKPLCQDREVOSFTGUHMINJWZYX";
var s = 0;
for( i = 1; i <= 13; i += 2 )
s += setpari.indexOf( set2.charAt( set1.indexOf( cf.charAt(i) )));
for( i = 0; i <= 14; i += 2 )
s += setdisp.indexOf( set2.charAt( set1.indexOf( cf.charAt(i) )));
So, I've "translated" it to:
SET set1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
SET set2 = "ABCDEFGHIJABCDEFGHIJKLMNOPQRSTUVWXYZ";
SET setpari = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
SET setdisp = "BAKPLCQDREVOSFTGUHMINJWZYX";
SET s=0;
SET v_counter = 0;
WHILE v_counter < 14
DO
SET set1IndexOf = POSITION(SUBSTR(codFisc, v_counter+1, 1) IN set1);
SET s = s + POSITION(SUBSTR(set2, set1IndexOf, 1) IN setpari) -1;
SET v_counter=v_counter+2;
END WHILE;
SET v_counter = 0;
WHILE v_counter < 15
DO
SET set1IndexOf = POSITION(SUBSTR(codFisc, v_counter+1, 1) IN set1);
SET s = s + POSITION(SUBSTR(set2, set1IndexOf, 1) IN setdisp) -1;
SET v_counter=v_counter+2;
END WHILE;
But the MySQL function returns a wrong result.
I know that charAt and indexOf are zero-based indexes, while SUBSTR and POSITION are one-based indexes. So I've incremented the v_counter but it is still wrong.
I can't see any difference between the two codes, but there's a bug somewhere.
Anyone can help me, please?
Thanks

ORDER BY Color with Hex Code as a criterio in MySQL

I have a table that contains color options for a product. The color options include a hex color code, which is used to generate the UI (HTML).
I would like to sort the rows so that the colors in the UI look like a rainbow, instead of the current order that sorts based off of the Name of the color (not very useful).
Here is what my query looks like. I get the R G B decimal values from the hex code. I just don't know how to order it.
I've looked into color difference algorithms. They seem more useful to compare 2 colors' similarity, not sort.
I'm using MySQL:
select a.*, (a.c_r + a.c_g + a.c_b) color_sum
from (
select co.customization_option_id,
co.designer_image_url,
concat(co.name, " (",cog.name, ")") name,
co.customization_option_group_id gr,
designer_hex_color,
conv(substr(designer_hex_color, 1, 2), 16, 10) c_r,
conv(substr(designer_hex_color, 3, 2), 16, 10) c_g,
conv(substr(designer_hex_color, 5, 2), 16, 10) c_b
from customization_options co
left join customization_option_groups cog
on cog.id = co.customization_option_group_id
where co.customization_id = 155
and co.customization_option_group_id
in (1,2,3,4)) a
order by ????
You want to sort hex codes by wavelength, this roughly maps onto the hue-value. Given a hexcode as a six character string: RRGGBB.
You just need to make a function that takes in a hexcode string and outputs the hue value, here's the formula from this Math.SO answer:
R' = R/255
G' = G/255
B' = B/255
Cmax = max(R', G', B')
Cmin = min(R', G', B')
Δ = Cmax - Cmin
I wanted to see if this would work, so I whipped up a sample program in Ruby, it samples 200 random colors uniformly from RGB-space, and sorts them, the output looks like a rainbow!
Here's the Ruby source:
require 'paint'
def hex_to_rgb(hex)
/(?<r>..)(?<g>..)(?<b>..)/ =~ hex
[r,g,b].map {|cs| cs.to_i(16) }
end
def rgb_to_hue(r,g,b)
# normalize r, g and b
r_ = r / 255.0
g_ = g / 255.0
b_ = b / 255.0
c_min = [r_,g_,b_].min
c_max = [r_,g_,b_].max
delta = (c_max - c_min).to_f
# compute hue
hue = 60 * ((g_ - b_)/delta % 6) if c_max == r_
hue = 60 * ((b_ - r_)/delta + 2) if c_max == g_
hue = 60 * ((r_ - g_)/delta + 4) if c_max == b_
return hue
end
# sample uniformly at random from RGB space
colors = 200.times.map { (0..255).to_a.sample(3).map { |i| i.to_s(16).rjust(2, '0')}.join }
# sort by hue
colors.sort_by { |color| rgb_to_hue(*hex_to_rgb(color)) }.each do |color|
puts Paint[color, color]
end
Note, make sure to gem install paint to get the colored text output.
Here's the output:
It should be relatively straight-forward to write this as a SQL user-defined function and ORDER BY RGB_to_HUE(hex_color_code), however, my SQL knowledge is pretty basic.
EDIT: I posted this question on dba.SE about converting the Ruby to a SQL user defined function.
This is based on the answer by #dliff. I initially edited it, but it turns out my edit was rejected saying "it should have been written as a comment or an answer". Seeing this would be too large to post as a comment, here goes.
The reason for editing (and now posting) is this: there seems to be a problem with colors like 808080 because their R, G and B channels are equal. If one needs this to sort or group colors and keep the passed grayscale/non-colors separate, that answer won't work, so I edited it.
DELIMITER $$
DROP FUNCTION IF EXISTS `hex_to_hue`$$
CREATE FUNCTION `hex_to_hue`(HEX VARCHAR(6)) RETURNS FLOAT
BEGIN
DECLARE r FLOAT;
DECLARE b FLOAT;
DECLARE g FLOAT;
DECLARE MIN FLOAT;
DECLARE MAX FLOAT;
DECLARE delta FLOAT;
DECLARE hue FLOAT;
IF(HEX = '') THEN
RETURN NULL;
END IF;
SET r = CONV(SUBSTR(HEX, 1, 2), 16, 10)/255.0;
SET g = CONV(SUBSTR(HEX, 3, 2), 16, 10)/255.0;
SET b = CONV(SUBSTR(HEX, 5, 2), 16, 10)/255.0;
SET MAX = GREATEST(r,g,b);
SET MIN = LEAST(r,g,b);
SET delta = MAX - MIN;
SET hue=
(CASE
WHEN MAX=r THEN (60 * ((g - b)/delta % 6))
WHEN MAX=g THEN (60 * ((b - r)/delta + 2))
WHEN MAX=b THEN (60 * ((r - g)/delta + 4))
ELSE NULL
END);
IF(ISNULL(hue)) THEN
SET hue=999;
END IF;
RETURN hue;
END$$
DELIMITER ;
Again, I initially wanted to edit the original answer, not post as a separate one.
If your products can have lots of color probably a good UI will require a color picker, normally those are rectangular, so not really something possible with the order by.
If the products have a manageable number of colors you have different choice, the easiest to implement is an order table, where for every possible color is defined an order position, this table can then be joined to your query, something like
SELECT ...
FROM (SELECT ...
...
...
, ci.color_order
FROM customization_options co
LEFT JOIN customization_option_groups cog
ON cog.id = co.customization_option_group_id
LEFT JOIN color_ ci
ON designer_hex_color = ci.color
WHERE ...) a
ORDER BY color_order
Another way to go is to transform the RGB color to hue and use this as the order.
There are different formula for this conversion, depending on wich order you want the primary color to have, all of them can be found on the wikipedia page for hue, I can update the answer to help you convert one of those to T-SQL, if needed.
MySQL function Hex to Hue. Based on Tobi's answer. :)
CREATE FUNCTION `hex_to_hue`(hex varchar(6)) RETURNS float
BEGIN
declare r float;
declare b float;
declare g float;
declare min float;
declare max float;
declare delta float;
declare hue float;
set r = conv(substr(hex, 1, 2), 16, 10)/255.0;
set g = conv(substr(hex, 3, 2), 16, 10)/255.0;
set b = conv(substr(hex, 5, 2), 16, 10)/255.0;
set max = greatest(r,g,b);
set min = least(r,g,b);
set delta = max - min;
set hue=
(case
when max=r then (60 * ((g - b)/delta % 6))
when max=g then (60 * ((b - r)/delta + 2))
when max=b then (60 * ((r - g)/delta + 4))
else null
end);
RETURN hue;
END

Mysql: Trim number to be between X and Y MIN/MAX

I don't know the terms or the exact word used for this, but I'm looking for a function inside MySQL that can trim a number to be between X and Y.
For example, something like this:
SELECT TRIM_NUMBER(NUMBER, MIN, MAX);
If I do: TRIM_NUMBER(50, 1, 100) it should be fine, and return 50.
But if I do something like: TRIM_NUMBER(999, 1, 100) it should return 100, because it's the MAX value I'v set it to. The same thing should apply for MIN.
Is there any such function included in MySQL ?
Try this
SELECT GREATEST(LEAST(NUMBER, 100), 1);
There isn't anything built in, but your code it yourself, like this:
select if(num < 1, 1, if (num > 100, 100, num))
If you want the "overkill" of a function, here's a suggestion:
create function trimNumber(x double, floorValue double, ceilValue double) returns double
begin
declare ans double;
if x >= floorValue and x <= ceilValue then
set ans = x;
else
if x < floorValue then
set ans = floorValue;
else
set ans = ceilValue;
end if;
end if;
return ans;
end
I think you are looking for something like that(from MySQL Reference Manual):
http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#operator_between
Just adding this because it is the ANSI standard way of expressing this:
select (case when num < 1 then 1
when num > 100 then 100
else num
end) as TrimmedNum

Determining cardinal (compass) direction between points

Is there a way to know in SQL Server 2008R2 if a point is at the south,east,etc...of another point?
For example, I have an origin point(lat1,lng1) and I want to know where point(lat2,lng2) is located from that origin: north, west,etc...
I'm trying to construct a wind rose graph and this might be useful to me.
I came up with a way of calculating the bearing using a fairly straightforward use of standard SQL functions. The ATAN function does most of the real work; the two CASE statements are just special case corrections. 1 is the source and 2 is the destination.
atan(([Longitude2]-[Longitude1])/(10e-10+[Latitude2]-[Latitude1]))*360/pi()/2
+case when [Latitude2]<[Latitude1] then 180 else 0 end
+case when [Longitude2]<[Longitude1] and [Latitude2]>[Latitude1] then 360 else 0 end
In order to calculate the bearing between two coordinates while using the Geography type in SQL Server 2008 R2, you can use this function:
CREATE FUNCTION [dbo].[CalculateBearing]
(
#pointA as geography
,#pointB as geography
)
RETURNS decimal(18,12)
AS
BEGIN
-- Declare the return variable
DECLARE #bearing decimal(18,12)
-- Declare the local variables
DECLARE #x decimal(18,12)
DECLARE #y decimal(18,12)
DECLARE #dLat decimal(18,12)
DECLARE #dLong decimal(18,12)
DECLARE #rLat1 decimal(18,12)
DECLARE #rLat2 decimal(18,12)
IF(#pointA.STIsEmpty() = 1 OR #pointB.STIsEmpty() = 1)
set #bearing = null
ELSE
BEGIN
-- Calculate delta between coordinates
SET #dLat = RADIANS(#pointB.Lat - #pointA.Lat)
SET #dLong = RADIANS(#pointB.Long - #pointA.Long)
-- Calculate latitude as radians
SET #rLat1 = RADIANS(#pointA.Lat)
SET #rLat2 = RADIANS(#pointB.Lat)
SET #y = SIN(#dLong)*COS(#rLat2)
SET #x = COS(#rLat1)*SIN(#rLat2)-SIN(#rLat1)*COS(#rlat2)*COS(#dLong)
IF (#x = 0 and #y = 0)
SET #bearing = null
ELSE
BEGIN
SET #bearing = CAST((DEGREES(ATN2(#y,#x)) + 360) as decimal(18,12)) % 360
END
END
-- Return the result of the function
RETURN #bearing
END
GO
And after this, you can use this function like this:
DECLARE #pointA as geography
DECLARE #pointB as geography
SET #pointA = geography::STGeomFromText('POINT(3 45)', 4326)
SET #pointB = geography::STGeomFromText('POINT(4 47)', 4326)
SELECT [dbo].[CalculateBearing](#pointA, #pointB)
UPDATE: Adding a schema
This morning I had need for this functionality to provide the range and cardinal direction to users when searching for nearby orders in our system. I came to Nicolas's answer here and it got me most of the way there. I created a second function that uses Nicolas's to get myself an abbreviated cardinal direction (N, NE, E, etc) for my UI.
Using Nicolas's bearing calculation provided here combined with values from https://en.wikipedia.org/wiki/Points_of_the_compass to determine ranges for each cardinal direction,
CREATE FUNCTION [dbo].[CalculateCardinalDirection]
(
#pointA as geography
,#pointB as geography
)
RETURNS varchar(2)
AS
BEGIN
DECLARE #bearing decimal(18,12)
-- Bearing calculation provided by http://stackoverflow.com/a/14781032/4142441
SELECT #bearing = dbo.CalculateBearing(#pointA, #pointB)
RETURN CASE WHEN #bearing BETWEEN 0 AND 22.5 THEN 'N'
WHEN #bearing BETWEEN 22.5 AND 67.5 THEN 'NE'
WHEN #bearing BETWEEN 67.5 AND 112.5 THEN 'E'
WHEN #bearing BETWEEN 112.5 AND 157.5 THEN 'SE'
WHEN #bearing BETWEEN 157.5 AND 202.5 THEN 'S'
WHEN #bearing BETWEEN 202.5 AND 247.5 THEN 'SW'
WHEN #bearing BETWEEN 247.5 AND 292.5 THEN 'W'
WHEN #bearing BETWEEN 292.5 AND 337.5 THEN 'NW'
ELSE 'N' -- Catches NULL bearings and the 337.5 to 360.0 range
END
END
GO
If the points data type are "Geometry" (Like UTM Coordinate System), you may use the following formula:
DEGREES(ATAN((X2-X1)/(Y2-Y1)))
+case when Y2<Y1 then 180 else 0 end
+case when Y2>Y1 and X2<X1 then 360 else 0 end
Here is the schema for more clarification:
X=X2-X1 and Y=Y2-Y1. A formula that gives the bearing clockwise from 0 (positive Y axis) to 360 degrees.
f(X,Y)=180-90*(1+SIGN(Y))*(1-SIGN(X^2))-45*(2+SIGN(Y))*SIGN(X)-180/PI()*SIGN(Y*X)*ATAN((ABS(Y)-ABS(X))/(ABS(Y)+ABS(X)))

mysql function to pretty print sizes (pg_size_pretty equivialent)?

postgresql have the pg_size_pretty() convenience function:
> select pg_size_pretty(100000);
pg_size_pretty
----------------
98 kB
Does MySQL have something similar ?
If not, before I make my own have anyone already made this, that they can share ?
There is no shipped function like that in MySQL.
So, I just created one : function pretty_size
https://github.com/lqez/pastebin/blob/master/mysql/pretty_size.sql
CREATE FUNCTION pretty_size( size DOUBLE )
RETURNS VARCHAR(255) DETERMINISTIC
BEGIN
DECLARE c INT DEFAULT 0;
DECLARE unit INT DEFAULT 1000;
DECLARE unitChar CHAR(6) DEFAULT 'KMGTPE';
DECLARE binaryPrefix BOOLEAN DEFAULT 1;
/* Set binaryPrefix = 1 to use binary unit & prefix */
/* See IEC 60027-2 A.2 and ISO/IEC 80000 */
IF binaryPrefix = 1 THEN
SET unit = 1024;
END IF;
WHILE size >= unit AND c < 6 DO
SET size = size / unit;
SET c = c + 1;
END WHILE;
/* Under 1K, just add 'Byte(s)' */
IF c = 0 THEN
RETURN CONCAT( size, ' B' );
END IF;
/* Modify as your taste */
RETURN CONCAT(
FORMAT( size, 2 ),
' ',
SUBSTR( unitChar, c, 1 ),
IF( binaryPrefix, 'iB', 'B' )
);
END $$
DELIMITER ;
pg_size_pretty
will give you Kb or MB or GB ,etc according to the internal code ,and you wont operate or sum this result ...
better use :
pg_relation_size :
returns the on-disk size in bytes ,so you can convert it to the format (kb,mb,gb) that you want.