I am new to Spatialite. I have following query:
select A.*
from linka as A, pointa as B
where Contains(Buffer(B.Geometry, 100), A.Geometry)
I actually want to create 100 meters buffer and get to know which are the link's are contained by it.
I can able to find the inserted '100' is actually degree value and it's giving me output which are coming in this range.
I can put the degree value also in my query but the transformation from degree to meters/kilometers is not same all around the world.
I gone through many sites and able to know 1 degree = 110 KM approx.
but from GIS expert and some reference sites also get to know at each pole on earth it's different.
For instance, the difference at Alta/Norway between metrical x and y for planar approximation is 34 km in x direction equal 111 km in y direction. The buffer looks similar to this while using geographic coordinates:
http://extremelysatisfactorytotalitarianism.com/blog/wp-content/uploads/2010/08/tissot_indicatrix_equirectangular_proj.png
I build software which convert geographical data to geometrical (X, Y -coordinate format) data and make transformation where Spatiallite can understand.
I also trying to read regarding SRID things but not able to understand how to insert it into my query.
temporary transform your geometry to a metric projection (eg UTM)
if i assume your current projection is WGS84 try the following statment
transform (buffer (transform (B.geometry, #projection), #dist), 4326))
-in #projection: your new projection, eg: 32631 for WGS 84 / UTM zone 31N (choose the projection that fits your Zone)
-in #dist: distance in meters
(4326 for WGS84)
If You are using SQL server 2008 or later, You should be able to use spatial types
lets assume linka contains geography column, and its name is geo, and it contains Points
dont forget to create spatial index !
try this
DECLARE #buffer geography = geography::Point( 1.234, 5.678, 4326 );
DECLARE #distance float = 100.0;
SELECT * from linka
WHERE linka.geo.STDistance(#buffer) < #distance
Related
I have the centroid of an operation, the width of the operation (assumed to be on both sides) and the distance of the operation. What is the most efficient way to make a bounding box in Postgres with this data?
For example:
My hypothetical centroid is 0,0. The operation is 4 feet wide on each side. The operation is also 50 feet long. What is the best way for me to create a bounding box from this data?
Assuming your hypothetical coordinate pair is encoded in a spatial reference system that has foot as unit, such as NAD83, you just need to create a 4ft buffer and calculate its BBOX using ST_Envelope or ST_Extent:
SELECT ST_Envelope(ST_Buffer('SRID=[YOUR-SRS];POINT (0 0)',4));
SELECT ST_Extent(ST_Buffer('SRID=[YOUR-SRS];POINT (0 0)',4));
Another option is to use the geography data type (metre as unit) to create the buffer and then cast it again to geometry to create the BBOX, e.g.
SELECT ST_Envelope(ST_Buffer('POINT (0 0)'::geography,4*3.2808)::geometry)
You can also use ST_Project to define the corners manually and use ST_MakeBox2D to create your BBOX:
SELECT
ST_MakeBox2D(
ST_Project('POINT(0 0)'::geography,4*3.2808,radians(225.0))::geometry,
ST_Project('POINT(0 0)'::geography,4*3.2808,radians(45.0))::geometry
)::geometry
Demo: db<>fiddle
WITH j (poi) AS (VALUES ('POINT(-4.50 54.15)'::geography))
-- 1. a random point, e.g. from ST_Centroid.
SELECT poi::geometry FROM j
UNION
-- 2. a 4ft buffer around the point of query 1
SELECT ST_Buffer(poi,4*3.2808) FROM j
UNION
-- 3. an envelope over the buffer created in query 2 (outer bbox)
SELECT ST_Envelope(ST_Buffer(poi,4*3.2808)::geometry) FROM j
UNION
-- 4. Inner bbox created based on a lower left (azimuth 225°)
-- and a up right (azimuth 45°) corner 4ft away from the POI.
SELECT
ST_MakeBox2D(
ST_Project(poi,4*3.2808,radians(225.0))::geometry,
ST_Project(poi,4*3.2808,radians(45.0))::geometry
)::geometry
FROM j;
Further reading:
Buffers in PostGIS
Caculate point 50 miles away (North, 45% NE, 45% SW)
Increase the BBOX size but keep the ratio (Lon/Lat)
How to extend the polygon to a certain distance?
I create a convex hull around the multipoint. But I need to extend the range to several kilometers. At least in theory.
http://img.radiokot.ru/files/21274/1oykzc5pez.png
Assuming that you're able to get a convex hull (which maybe you're using ConvexHullAggregate!), STBuffer() should do what you want.
declare #hull geography = «your value here»;
select #hull.STBuffer(10000); -- 10 km buffer
NB: the 10000 may need to change based on the SRID that you're using since SRIDs have units of distance baked into them inherently. But SRID 4326 is what's used in the docs most often and the native unit for that SRID is meters. So 10 km → 10000 m.
Build outer bisector vector in every vertex (as sum of normalized normals na and nb of two neighbor edges) and normalize it
bis = na + nb
bis = bis / Length(bis)
Make length of bisector to provide needed distance as
l = d / Cos(fi/2)
where d is offset, and fi is angle between vectors na and nb.
fi = atan2(crossproduct(na,nb), dotproduct(na,nb))
or without trigonometric functions:
l = d / Sqrt((1 + dotproduct(na,nb))/2)
And find offset polygon vertex:
P' = P + l * bis
I am running following SQL query in my JAVA Spring server. This query works perfect for almost all coordinates except for one specific pair c = <23.065079, 72.511478> (= to_lat, to_long):
SELECT *
FROM karpool.ride
WHERE Acos(Sin(Radians(23.065079)) * Sin(Radians(to_lat)) +
Cos(Radians(23.065079)) * Cos(Radians(to_lat)) *
Cos(Radians(to_lon) - Radians(72.511478))) * 6371 <= 10;
My database has many locations within 10 km distance to c. With the above query, I get all those locations' distances, except for the one which exactly matches with c. The distance returned should be 0 in that case, but the query fails.
Is this an SQL issue or is there something wrong with the formula?
This is most probably due to floating point accuracy problems.
First of all, the used formula is the Great circle distance formula:
Let φ1,λ1 and φ1,λ2 be the geographical latitude and longitude of two points 1 and 2, and Δφ,Δλ their absolute differences; then Δσ, the central angle between them, is given by the spherical law of cosines:
Δσ = arccos ( sin φ1 ∙ sin φ2 + cos φ1 ∙ cos φ2 ∙ cos (Δλ) ).
The distance d, i.e. the arc length, for a sphere of radius r and Δσ given in radians
d = r Δσ.
Now if the two points are the same, then Δλ = 0, and thus cos(Δλ) = cos(0) = 1, and the first formula reduces to:
Δσ = arccos (sin φ ∙ sin φ + cos φ ∙ cos φ).
The argument to arccos has become the Pythagorean trigonometric identity, and thus equals 1.
So the above reduces to:
Δσ = arccos (1).
The problem
The domain of the arccosine is: −1 ≤ x ≤ 1, so with the value 1 we are at the boundary of the domain.
As the value of 1 was the result of several floating point operations (sines, cosines, multiplications), it could occur that the value is not exactly 1, but something like 1.0000000000004. That poses a problem, for that value is out of range for calculating the arccosine. Database engines respond differently to this situation:
SQL Server will raise an exception:
An invalid floating point operation occurred.
MySql will just evaluate the expression as null.
The solution
Somehow the argument passed to the arccosine should be made to stay in the range −1 ≤ x ≤ 1. One way of doing this, is to round the argument to a number of decimals that is large enough to keep some precision, but small enough to round away any excess outside this range caused by floating point operations.
Most database engines have a round function to which a second argument can be provided to specify the number of digits to keep, and so the SQL would look like this (keeping 6 decimals):
SELECT *
FROM karpool.ride
WHERE Acos(Round(
Sin(Radians(23.065079)) * Sin(Radians(to_lat)) +
Cos(Radians(23.065079)) * Cos(Radians(to_lat)) *
Cos(Radians(to_lon) - Radians(72.511478)),
6
)) * 6371 <= 10;
Alternatively, you could use the functions greatest and least, which some database engines provide, to turn any excess value to 1 (or -1):
SELECT *
FROM karpool.ride
WHERE Acos(Greatest(Least(
Sin(Radians(23.065079)) * Sin(Radians(to_lat)) +
Cos(Radians(23.065079)) * Cos(Radians(to_lat)) *
Cos(Radians(to_lon) - Radians(72.511478)),
1), -1)
) * 6371 <= 10;
Note that SQL Server does not provide greatest/least functions. A question to overcome this has several answers.
Pretty straight forward question, I want to store feet and inches in 1 column using a decimal, but I dont want it truncated like how the float type does.
Store all your data in MKS (metric). When presenting and storing convert the data into an international standard. and store it in a decimal type.
Thus if your tool to gather the data is in 6'2" format convert it into cm and save in your data table. Then reverse this for display.
By saving in a standard format decimal cm. finding people with the same range of height is easier, where as if Ft and In are in separate columns ranges are really hard.
The imperial unit system still used in Myanmar, Liberia and that one country in North America is unfortunately not very arithmetics-friendly. There is no native data-type to handle the strange base12/base3/base1860 math for it.
You should really use the much more widely-used metric system and use a FLOAT or DECIMAL value representing meters.
However, when you really want to stay with the imperial system, you should store the value in inch and do the conversation to feet + inch on the GUI level.
Decimal lets you store an exact precision.
The question is if you want to store it in feet or inches.
If inches then:
feet * 12 + inches
If feet then:
feet + (inches / 12)
If inches the conversion back
declare #inches dec(8,4)
declare #indesinfoot as dec(8,4);
set #indesinfoot = 12;
set #inches = (12*#indesinfoot) + 6.25;
print #inches;
SELECT cast(#inches/#indesinfoot as Int) as feet,
inches % #indesinfoot AS inches;
I would go with inches as you can get some rounding error with division but not with multiplication.
Use DECIMAL Data type
It lets you specify the precision you need for your specific needs, without truncating like FLOAT
NUMERIC() or DECIMAL() will work for a single column answer, but you'll need to decide if you're storing feet or inches. These fields use precise math and accurate storage.
If you must store feet and inches, you'll need to either define your own datatype (which is fairly complicated) or use two NUMERIC() or DECIMAL() fields.
Not that you'd ever run into precision problems with feet or inches with FLOAT when measuring something the size of a human being. You'd be off by a hair. Literally.
Example procedure to convert the old English system to metric. No error checking but it will give you an idea how to convert data as entered into data for storage.
CREATE PROCEDURE usp_ConvertHeight
#Input varchar(10)
AS
BEGIN
DECLARE
#FT AS DECIMAL(18,10) = 0,
#IN AS DECIMAL(18,10) = 0,
#CM AS DECIMAL(18,10)
SELECT #FT = CAST(left(#Input,CHARINDEX('''',#Input,1) - 1) AS DECIMAL(18,10));
SELECT #IN = CAST(REPLACE(SUBSTRING(#Input,CHARINDEX('''',#Input,1) + 1,10),'"','')AS DECIMAL(18,10));
SET #CM = 2.54 * ((12 * #FT) + #IN);
SELECT #CM
END
I suggest to store the data in single unit,either inches or in cm.
Ideally give the user option to enter it in any format. Convert and show the user information in all the possible units like in feets and inches and cm.
I'm working with billions of rows of data, and each row has an associated start latitude/longitude, and end latitude/longitude. I need to calculate the distance between each start/end point - but it is taking an extremely long time.
I really need to make what I'm doing more efficient.
Currently I use a function (below) to calculate the hypotenuse between points. Is there some way to make this more efficient?
I should say that I have already tried casting the lat/longs as spatial geographies and using SQL built in STDistance() functions (not indexed), but this was even slower.
Any help would be much appreciated. I'm hoping there is some way to speed up the function, even if it degrades accuracy a little (nearest 100m is probably ok).
Thanks in advance!
DECLARE #l_distance_m float
, #l_long_start FLOAT
, #l_long_end FLOAT
, #l_lat_start FLOAT
, #l_lat_end FLOAT
, #l_x_diff FLOAT
, #l_y_diff FLOAT
SET #l_lat_start = #lat_start
SET #l_long_start = #long_start
SET #l_lat_end = #lat_end
SET #l_long_end = #long_end
-- NOTE 2 x PI() x (radius of earth) / 360 = 111
SET #l_y_diff = 111 * (#l_lat_end - #l_lat_start)
SET #l_x_diff = 111 * (#l_long_end - #l_long_start) * COS(RADIANS((#l_lat_end + #l_lat_start) / 2))
SET #l_distance_m = 1000 * SQRT(#l_x_diff * #l_x_diff + #l_y_diff * #l_y_diff)
RETURN #l_distance_m
I haven't done any SQL programming since around 1994, however I'd make the following observations:The formula that you're using is a formula that works as long as the distances between your coordinates doesn't get too big. It'll have big errors for working out the distance between e.g. New York and Singapore, but for working out the distance between New York and Boston it should be fine to within 100m.I don't think there's any approximation formula that would be faster, however I can see some minor implementation improvements that might speed it up such as (1) why do you bother to assign #l_lat_start from #lat_start, can't you just use #lat_start directly (and same for #long_start, #lat_end, #long_end), (2) Instead of having 111 in the formulas for #l_y_diff and #l_x_diff, you could get rid of it there hence saving a multiplication, and instead of 1000 in the formula for #l_distance_m you could have 111000, (3) using COS(RADIANS(#l_lat_end)) or COS(RADIANS(#l_lat_start)) won't degrade the accuracy as long as the points aren't too far away, or if the points are all within the same city you could just work out the cosine of any point in the cityApart from that, I think you'd need to look at other ideas such as creating a table with the results, and whenever points are added/deleted from the table, updating the results table at that time.