this is an oldie, but i cannot seem to find a solution.
When i want to do an st_transform on a 900913 coordinate to a 4326 system, the y coordinate shifts.
example:
SELECT
AsText(
Transform(
Transform(
GeomFromText( 'POINT( 449760.25168159 6790560.4594059 )', 900913),
4326
),
900913
)
)
here the original 900913 stating point is st_stransformed to 4326 and back to 900913. the result is not the original point, y differs. (i will insert the result later, i don't have it here).
i tried altering the proj4text for 4326, adding +nadgrids=#null like i read somewhere
the proj4text for srid 4326 is currently:
"select proj4text from spatial_ref_sys where srid=4326"
+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs
the proj4text for srid 900913 is currently:
"select proj4text from spatial_ref_sys where srid=900913"
+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m
+nadgrids=#null +no_defs
i also tried doing a projection from 900913 to another projection to 4326, but i get the exact same point as a direct transformation from 900913 to 4326.
anyone any ideas?
EJ
EPSG:900913 is ill-defined projection. You should use EPSG:3857 instead, which should be exactly the same Spherical Mercator but a standardized one.
Any reprojection is a lossy operation. Converting a coordinate back and forth is going to make noise in last binary digits, which are usually sub-millimeter error.
What version of PostGIS are you using? I've tried the query with this configuration: POSTGIS="1.5.2" GEOS="3.2.2-CAPI-1.6.2" PROJ="Rel. 4.7.1, 23 September 2009" and it works fine.
Despite that, I've red some problems involving 900913 coordinates.
Related
My requirement is to calculate the distance between two locations on a given map using mysql. I found a function in mysql named ST_Distance_Sphere which returns the minimum spherical distance between two locations and/or multi locations on a sphere in meters.
When I computed the distance between two locations using ST_Distance_Sphere and the lat_lng_distance function , I found that the ST_Distance_Sphere is not giving the same distance as that of the lat_lng_distance function.
My lat_lng_distance function code is as follows
CREATE FUNCTION `lat_lng_distance` (lat1 FLOAT, lng1 FLOAT, lat2 FLOAT, lng2 FLOAT)
RETURNS FLOAT
DETERMINISTIC
BEGIN
RETURN 6371 * 2 * ASIN(SQRT(
POWER(SIN((lat1 - abs(lat2)) * pi()/180 / 2),
2) + COS(lat1 * pi()/180 ) * COS(abs(lat2) *
pi()/180) * POWER(SIN((lng1 - lng2) *
pi()/180 / 2), 2) ));
END
The two locations ((38.898556,-77.037852),(38.897147,-77.043934)) passed to the ST_Distance_Sphere and lat_lng_distance function is as follows
SET #pt1 = ST_GeomFromText('POINT (38.898556 -77.037852)');
SET #pt2 = ST_GeomFromText('POINT (38.897147 -77.043934 )');
SELECT ST_Distance_Sphere(#pt1, #pt2)/1000,lat_lng_distance(38.898556,-77.037852,38.897147,-77.043934 );
The Results Obtained is as follows
I checked the distance between the two locations on google maps and found that lat_lng_distance is close to the actual distance between the two locations. Can someone let me know why is the ST_Distance_Sphere not giving accurate distance between two locations?
ST_DISTANCE_SPHERE requires points to be expressed as POINT(longitude, latitude), you have them reversed in your code
set #lat1 = 38.898556;
set #lon1 = -77.037852;
set #lat2 = 38.897147;
set #lon2 = -77.043934;
SET #pt1 = point(#lon1, #lat1);
SET #pt2 = point(#lon2, #lat2);
SELECT ST_Distance_Sphere(#pt1, #pt2)/1000,
lat_lng_distance(#lat1,#lon1,#lat2,#lon2);
+-------------------------------------+-------------------------------------------+
| ST_Distance_Sphere(#pt1, #pt2)/1000 | lat_lng_distance(#lat1,#lon1,#lat2,#lon2) |
+-------------------------------------+-------------------------------------------+
| 0.549154584458455 | 0.5496311783790588 |
+-------------------------------------+-------------------------------------------+
This gives a result that is much closer to the value returned by your function.
For all who are working with MYSQL 8:
For all mysql geolocation functions there must be the right SRID used, otherwise you won't get the right results.
Most commenly used is SRID 4326 (GPS Coordinates, Google Earth) AND SRID 3857 (used on Google Maps, OpenStreetMap, and most other web maps).
Example of a correct distance calculation between two points:
SELECT ST_Distance(ST_GeomFromText('POINT(51.513 -0.08)', 4326), ST_GeomFromText('POINT(37.745 -122.4383)', 4326)) / 1000 AS km;
Here is a good explanation of this topic:
https://medium.com/maatwebsite/the-best-way-to-locate-in-mysql-8-e47a59892443
There are some very good explanations from the mysqlserverteam:
https://mysqlserverteam.com/spatial-reference-systems-in-mysql-8-0/
https://mysqlserverteam.com/geography-in-mysql-8-0/
First of all, you could not use default SRID of 0 to do any calculations. When you use geometry from text function you have to provide 4326 (SRID that is degrees) as this is what your input format is. MYSQL might not care about it, but it should done as every serious GIS database does care and demands that input SRID was specified.
Second longitude is X and latitude is Y (not another way around)
SET #pt1 = ST_GeomFromText('POINT (-77.037852 38.898556 )', 4326);
SET #pt2 = ST_GeomFromText('POINT (-77.043934 38.897147 )',4326);
Last but not least when you are calculating distance you must transform coordinates to a local most precise SRID available for the region you are.
For example SRID 2877 is used for USA (where according your coordinates you are).
MYSQL ST_Distance_Sphere function does not care about input SRID and always return results in meters.
However it is not generally right and all other database use designated SRID units of measures applicable to it.
Bellow I am trying to do things right and transforming SRID to 2877 even MYSQL would work the same way if we left everything as 4326 (google mercator).
For 2877 PostGRES would return results in feet for the same query but MYSQL is still giving back meters. So output is devided by 1609 and we are getting the correct result of around 0.34 miles. It is a correct value as was tested using different methods
SELECT ST_Distance_Sphere(ST_GeomFromText(ST_AsText(#pt1),2877), ST_GeomFromText(ST_AsText(#pt2),2877))/1609.344;
As an aside, MySQL internally implements this with an obscure and dated constant. So it really depends on your definition of accurate.
ST_Distance_Sphere
So in essence, the radius in MySQL was lifted from a lazy-copy-job from PostGIS that converted a radius in miles to meters from an obscure constant from a random 20-year old PostgreSQL module.
select *
from zones z
where st_intersects(z.geom, st_Buffer(ST_SetSRID(ST_Point( 2.336031, 48.863172), 3857),1));
It looks like the '1' in the st_buffer is being interpreted as degrees, I need to find geometries within n meters of a given point.
I did find a reference to a new function (st_Buffer_Meters) but that didn't seem to do anything different.
The actual query that ended up working was:
select nom
from zones s
where ST_DWITHIN(Geography(ST_Transform(s.geom,4326)), ST_Point($1, $2) ,$3);`
Where $1 is Longitude, $2 is Latitude and $3 is distance in meters.
In addition I changed my imported data from SRID 3857 to SRID 4326
Here's the gis.stackexchange question: https://gis.stackexchange.com/questions/118472/postgis-get-geometries-within-a-radius-of-n-meters-using-wgs84/118476#118476
And here's the 'This has been asked before link https://gis.stackexchange.com/questions/77688/postgis-get-the-points-that-are-x-meters-near-another-point-in-meters
First, I simplified your query to use a distance function, which will do the same thing as your query performs with fewer words. ST_Distance_Sphere will return the minimum distance between two geometries in meters. However I had some difficulty in using your SRID so I use ST_Transform to transform the SRID to 4326
select *
from zones z
where st_distance_sphere(z.geom,
ST_TRANSFORM(ST_SetSRID(ST_Point( 2.336031, 48.863172), 3857), 4326
)< 1000;
I have a point (x, y) with srid 900913. I transform it to srid 2180 and then again to srid 900913. Imo I should have the same point, but it differs. Why?
SELECT ST_X (ST_Transform(ST_Transform(ST_GeomFromText('POINT(21.01233628836129 52.23044648850736)', 900913), 2180), 900913)),
ST_Y(ST_Transform(ST_Transform(ST_GeomFromText('POINT(21.01233628836129 52.23044648850736)', 900913), 2180), 900913));
The quick answer why the two transformations were different is because of a common confusion of projection systems.
The coordinate POINT(21.01233628836129 52.23044648850736) on a Spherical Mercator projection is at 0°0'1.689"N 0°0'0.680"E, which is way outside Poland, which makes it difficult or typically impossible to reproject it to anything else.
The coordinates you were looking at are most likely longitude/latitude on WGS 84 (EPSG:4326).
I think this is the exercise you were attempting (LatLon_check in particular):
SELECT ST_AsLatLonText(geom) AS LatLonText,
ST_AsLatLonText(ST_Transform(ST_Transform(geom, 2180), 4326)) as LatLon_check,
ST_AsText(ST_Transform(geom, 2180)) AS Poland_CS92,
ST_AsText(ST_Transform(geom, 900913)) AS Spherical_Mercator
FROM ST_GeomFromText('POINT(21.01233628836129 52.23044648850736)', 4326) AS geom;
-[ RECORD 1 ]------+----------------------------------------
latlontext | 52°13'49.607"N 21°0'44.411"E
latlon_check | 52°13'49.607"N 21°0'44.411"E
poland_cs92 | POINT(637389.203455155 486840.46005323)
spherical_mercator | POINT(2339082.5759974 6841900.8700405)
I'm trying to query any locations within a specified distance from another location. The query is not the problem, but the distance returned by geography.STDistance is.
It seems STDistance makes fairly accurate calculations on locations close to the equator, but I need this to work with locations in the nordic countries. Norway, Sweden, Finland and so on...
According to my calculations, made on locations in northern Sweden, the distance is wrong by a factor of around 2.38?!
Expected result is 1070 meters and returned distance is 2537,28850694302 meters
My query looks like this:
DECLARE #g geography = geography::STGeomFromText('POINT(65.580254 22.179428)', 4326)
SELECT name, [pos].STSrid as srdi, [pos].STDistance(#g) as d
FROM [GPSCHAT].[dbo].[USERS]
and the "other location" has coordinates (65,578541 22,202286) (stored with SRID 4326)
I'm guessing this has to do with the distance from the equator (close to the polar circle), but there has to be a way to calculate this more accurately based on the Latitude or am i wrong?
It looks like you're creating your point using 'X, Y'.
When creating a point from text, use 'Y, X' instead.
Check out this MSDN Article for some more info.
Why don't you make use of another spatial reference identifier which fits better the earth curvature around your position. SRID 4326 might not been measured as accurate as other local referential systems
Is there a convention for whether GIS points in MySQL should be stored as POINT($latitude $longitude) or POINT($longitude $latitude)?
Having longitude correspond to X on a cartesian map would visually make more sense with north pointing up, but common parlance is to say "latitude and longitude."
In MySQL you will probably use the GeomFromText() function to insert data in a spatial field. This function uses the WKT (Well-Known Text) format to define the geometries, and in the POINT case, it is defined as:
POINT ($longitude $latitude)
The accepted answer is NOT CORRECT for working with GPS coordinates in MySQL 8+ and it will get into trouble (haven't tested it with previous version of MySQL).
TL;DR; Use 'POINT($lat $long)' as WKT string but POINT($long, $lat) with the POINT() function in MySQL 8+.
Full answer:
Using WKT notation as 'POINT($longitude $latitude)' while using SRID 4326 (the one you should use for GPS coordinates system) leads to incorrect distance calculations even if consistently used throughout the app. Read on for details.
For example, let's consider the direct distance between CN Tower in Toronto and One World Trade Center in NYC which is approx. 549,18km according to Google Maps.
GPS coordinates:
CN Tower: 43.64386666880877, -79.38670551139633
One World Trade Centre: 40.689321781458446, -74.04415571126154
Expected distance: 549.18km
Following query yields the correct result:
SELECT
ST_DISTANCE(
ST_GEOMFROMTEXT('POINT(40.689321781458446 -74.04415571126154)', 4326),
ST_GEOMFROMTEXT('POINT(43.64386666880877 -79.38670551139633)', 4326),
'metre'
)
FROM DUAL;
-- results in 549902.432032006 meters which is around 549.9km (CORRECT)
However, if you provide longitude first in your WKT (as suggested in the accepted answer) you get a wrong distance calculated:
SELECT
ST_DISTANCE(
ST_GEOMFROMTEXT('POINT(-74.04415571126154 40.689321781458446)', 4326),
ST_GEOMFROMTEXT('POINT(-79.38670551139633 43.64386666880877)', 4326),
'metre'
)
FROM DUAL;
-- results in 601012.8595500318 which is around 601km (WRONG)
As you can see the POINT($long $lat) WKT string approach is incorrect and is off by approx. 51km compared to POINT($lat $long) approach which is almost 10% error. And it actually gets worse the farther you go.
Explanation:
It seems to happen because when MySQL considers a WKT string in the context of GPS coordinates it considers first argument as latitude and the second one as longitude. Try running the following query:
SELECT
ST_Latitude(ST_GEOMFROMTEXT('POINT(40.689321781458446 -74.04415571126154)',4326)) as latitude,
ST_Longitude(ST_GEOMFROMTEXT('POINT(40.689321781458446 -74.04415571126154)',4326)) as longitude
FROM dual;
-- results in
latitude, longitude
40.689321781458446,-74.04415571126154
Beware though, that the opposite is true when using the POINT(x, y) function instead of a WKT string!
Example:
SELECT
ST_DISTANCE(
ST_SRID(POINT(-74.04415571126154, 40.689321781458446), 4326),
ST_SRID(POINT(-79.38670551139633, 43.64386666880877), 4326),
'metre'
)
FROM DUAL;
-- results in 549902.432032006 meters which is around 549.9km (CORRECT)