Drawing a bézier curve over the equator - postgis

I'm attempting to draw a very long line from one point to another. ST_Project seems to give me the correct point to draw to, but when I use ST_MakeLine the line doesn't move east immediately from my starting point, and as such doesn't create the bézier curve I'd be expecting, but rather an arch.
In Google Maps if you draw a line using their measuring tool travelling 90° from the starting point, then it creates the expected bézier curve:
Whereas if I export my line to KML and import it into Google Maps, the line is more of an arch:
I'm using the following SQL to create my points and draw the two lines:
SELECT
ST_AsKML(
ST_Transform(
ST_Segmentize(
ST_MakeLine(
ST_Transform(
ST_SetSRID(ST_MakePoint(-0.3856949, 51.6443612), 4326),
953027
),
ST_Transform(
ST_SetSRID(
ST_AsText(
ST_Project(
ST_MakePoint(-0.3856949, 51.6443612),
2.00151e+07,
radians(90.0)
)
),
4326
),
953027
)
),
100000
),
4326
)
);

I think I've answered my own question by using explicit geometry types and NOT using ST_Segmentize as that function exists to create great circle arcs, which in this case is not what I wanted. As far as I understand it, great circle arcs are the shortest distances, such as aviation routes.
Using the below code I can create the bézier curves seen on Google Maps measuring tool:
SELECT
name,
ST_Distance(
ST_SetSRID(ST_MakePoint(0.1278, 51.5074), 4326),
area
) AS distance
FROM
countries
WHERE
(
ST_Intersects(
area,
ST_SetSRID(
ST_MakeLine(
ST_SetSRID(ST_MakePoint(0.1278, 51.5074), 4326) :: geometry,
ST_Project(
ST_SetSRID(ST_MakePoint(0.1278, 51.5074), 4326) :: geometry,
2.00151e+07,
pi() * 90 / 180.0
) :: geometry
),
4326
)
)
)
ORDER BY
ST_Distance(
ST_SetSRID(ST_MakePoint(0.1278, 51.5074), 4326),
area
);

Related

Geodetic coordinates: How to project a point on a line on the same meridian

Given a geodetic segment, e.g., from Brussels to Moscow, and a geodetic point, e.g., Berlin, which can be expressed in PostGIS as follows
select geography 'Linestring(4.35 50.85,37.617222 55.755833)', geography 'Point(13.405 52.52)';
I need to find the point on the segment that is exactly at the same meridian of the given point, i.e., their bearing is 0 degrees.
Any idea how to obtain this ?
For example, using PostGIS, I create a segment from Berlin to the North by taking Berlin's longitude with the maximum of the two latitudes of the initial segment and compute the intersection of the two segments as follows
select st_astext(st_intersection(geography 'Linestring(4.35 50.85,37.617222 55.755833)',
geography 'Linestring(13.405 52.52,13.405 55.755833)'))
st_astext
----------------------------------------------
POINT(13.407059592483968 53.163047143541235)
As can be seen the longitude is not exactly the same and the bearing would not be 0.
with test(inter) as (
select st_intersection(geography 'Linestring(4.35 50.85,37.617222 55.755833)',
geography 'Linestring(13.405 52.52,13.405 55.755833)') )
SELECT degrees(bearing(geography 'Point(13.405 52.52)', inter)) from test;
degrees
---------------------
0.11002408958628832
where bearing is computed using the traditional formula (e.g., https://gist.github.com/jeromer/2005586)
I find the postgis intersection result in you example surprising (and probably wrong). Doing the inverse of your query, it seems to me that there is a numerical precision issue:
select st_intersects(geography 'POINT(13.407059592484 53.1630471435412)', geography 'Linestring(13.405 52.52,13.405 55.755833)')
--FALSE
select st_intersects(geography 'POINT(13.405 53.1630471435412)', geography 'Linestring(13.405 52.52,13.405 55.755833)')
-- TRUE
This strangeness is caused by how intersections are computed when dealing with geographies.
The st_intersection doc says:
Geography: For geography this is really a thin wrapper around the geometry implementation. It first determines the best SRID that fits the bounding box of the 2 geography objects (if geography objects are within one half zone UTM but not same UTM will pick one of those) (favoring UTM or Lambert Azimuthal Equal Area (LAEA) north/south pole, and falling back on mercator in worst case scenario) and then intersection in that best fit planar spatial ref and retransforms back to WGS84 geography.
Using your coordinates and peeking at the code, we can see the that a LAEA projection is used. If you were to extend the south-north line, a world mercator projection would be used - and the result would be better!
select _ST_BestSRID(geography 'Linestring(4.35 50.85,37.617222 55.755833)',
geography 'Linestring(13.405 52.52,13.405 55.755833)');
_st_bestsrid
--------------
999247
(1 row)
select _ST_BestSRID(geography 'Linestring(4.35 50.85,37.617222 55.755833)',
geography 'Linestring(13.405 42.52,13.405 65.755833)');
_st_bestsrid
--------------
999000
(1 row)
select st_astext(st_intersection(geography 'Linestring(4.35 50.85,37.617222 55.755833)',
geography 'Linestring(13.405 42.52,13.405 65.755833)'));
st_astext
--------------------------------
POINT(13.405 52.2417230551345)
(1 row)
But even though, re-projection over the entire lines length is occuring, which can affect the output. The solution is therefore to subdivide the inputs into many small segments, so the reprojected lines are more aligned with the original ones
select st_astext(st_intersection( ST_Segmentize(geography 'Linestring(4.35 50.85,37.617222 55.755833)',1000),
ST_Segmentize(geography 'Linestring(13.405 52.52,13.405 55.755833)',1000)));
st_astext
------------------------------------------
POINT(13.4050000115689 53.2633912803655)
(1 row)
Berlin is located North of the line between Bruxelles and Moscow. So your two segments don't intersect each other. Instead of considering the line from Berlin to the highest latitude of the first segment you should directly consider a line going from the North pole to the South Pole, which gives you get the correct answer :
select st_astext(
st_intersection(
geography 'Linestring(4.35 50.85,37.617222 55.755833)',
geography 'Linestring(13.405 89.999999,13.405 -89.999999)'))
-- POINT(13.405 52.2417230551345)

How to get coordinates of a bounding box at a certain distance from a point in postGIS?

My goal is to define a bounding box of a circle of a given radius built around a point in postGIS.
Point, extent are ok but when I do this:
select ST_AsGeoJSON( st_extent( st_buffer( ST_SetSRID(ST_MakePoint(11.0120, 49.5897), 4326), 6000 ) ) );
I get
{"type":"Polygon","coordinates":[[[-5988.988,-5950.4103],
[-5988.988,6049.5897],
[6011.012,6049.5897],
[6011.012,-5950.4103],
[-5988.988,-5950.4103]]]}
when I was expecting something along the lines of (
{"type":"Polygon","coordinates":[[[10.929017, 49.535753],
[10.929017, 49.643646],
[11.094983, 49.643646],
[11.094983, 49.535753],
[10.929017, 49.535753]]]}
(handmade GeoJSON - might contain errors)
So - how do I get the postGIS SQL to output geographical coordinates instead of what looks like geometry?
If geometry is passed into ST_Buffer, then the second argument is degrees, not meters. To use meters for the second argument, cast the first argument to geography. Then cast it back to geometry for use with extent:
select
ST_AsGeoJSON(
st_extent(
st_buffer(
ST_SetSRID(
ST_MakePoint(11.0120, 49.5897),
4326
)::geography,
6000
)::geometry
)
);

Retrieving raster data by geographic location using Landsat and PostGIS

The project I am working on requires that I retrieve Landsat raster data at specific geographic (lon/lat) locations. After sifting through some tutorials and experimenting with GDAL, PostGIS, and QGIS, I successfully imported a GeoTIFF Landsat image into a PostGIS raster table and accessed values by geographic location from that table. However, there were a few issues in the result:
I do not understand the coordinate system being used by QGIS in its interface, as they range in the hundred thousands
The raster loaded into QGIS off the coast of Spain, rather than on top of Maine, USA as it was supposed to.
Here's some information about my process. I am fairly new to GIS in general, so I am almost certain theres a blatant error to be found here:
Download Landsat 8 GeoTIFF file from USGS GloVis
Rename the band 5 image to something more friendly to command ninja with.
Create postgres database for raster tables and run CREATE EXTENSION postgis;
Run gdalinfo LSSampleB5.TIF, printing the following output:
Driver: GTiff/GeoTIFF
Files: LSSampleB5Test2.TIF
Size is 7871, 7971
Coordinate System is:
PROJCS["WGS 84 / UTM zone 19N",
GEOGCS["WGS 84",
DATUM["WGS_1984",
SPHEROID["WGS 84",6378137,298.257223563,
AUTHORITY["EPSG","7030"]],
AUTHORITY["EPSG","6326"]],
PRIMEM["Greenwich",0,
AUTHORITY["EPSG","8901"]],
UNIT["degree",0.0174532925199433,
AUTHORITY["EPSG","9122"]],
AUTHORITY["EPSG","4326"]],
PROJECTION["Transverse_Mercator"],
PARAMETER["latitude_of_origin",0],
PARAMETER["central_meridian",-69],
PARAMETER["scale_factor",0.9996],
PARAMETER["false_easting",500000],
PARAMETER["false_northing",0],
UNIT["metre",1,
AUTHORITY["EPSG","9001"]],
AXIS["Easting",EAST],
AXIS["Northing",NORTH],
AUTHORITY["EPSG","32619"]]
Origin = (318285.000000000000000,5216715.000000000000000)
Pixel Size = (30.000000000000000,-30.000000000000000)
Metadata:
AREA_OR_POINT=Point
Image Structure Metadata:
INTERLEAVE=BAND
Corner Coordinates:
Upper Left ( 318285.000, 5216715.000) ( 71d23'37.53"W, 47d 4'44.12"N)
Lower Left ( 318285.000, 4977585.000) ( 71d18' 9.77"W, 44d55'42.53"N)
Upper Right ( 554415.000, 5216715.000) ( 68d16'58.41"W, 47d 6' 6.11"N)
Lower Right ( 554415.000, 4977585.000) ( 68d18'36.69"W, 44d56'58.62"N)
Center ( 436350.000, 5097150.000) ( 69d49'20.56"W, 46d 1'29.87"N)
Band 1 Block=7871x1 Type=UInt16, ColorInterp=Gray
I interpretted this output as EPSG 4326 format (which may be my crime), so I ran the following command to import the GeoTIFF as a PostGIS raster:
raster2pgsql -s 4326 -I LSSampleB5.TIF -F -t 50x50 -d | psql -U postgres rastertest
This successfully imported a new table. I then used QGIS to get a visual intuition of what was going on.
Under Database -> DB Manager -> PostGIS -> rastertest -> public I added my lssampleb5 to the canvas.
I created a new XYZ Connection in QGIS to add Google satillite hybrid images for reference. The url I used was https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z} with min and max zoom of 0 and 19 respectively.
Here is where I took note of the fact that the lssample layer landed off the coast of Spain on the Google Hybrid map.
I made sure both layers were on EPSG 4326 projection, no change.
Not too discouraged to move on, I tried a database query to get a single pixel value. Since my sample data landed near Spain, I used QGIS to sample a valid coordinate pair near there for the query. The query was:
SELECT rid, ST_Value(rast, 1, ST_SetSRID(ST_Point(448956,5041439), 4326)) as b5
FROM lssampleb5
WHERE ST_Intersects(rast, ST_SetSRID(ST_Point(448956,5041439), 4326)::geometry, 1);
This returned a valid row ID and an ST_VALUE of 5776. Trying coordinates outside the range displayed by QGIS resulted in no returned entries, which isn't unexpected.
So, first of all, I do not know what QGIS is using for its coordinate system. It's definitely not longitude and latitude in a raw form, but from my understanding, EPSG 4326 is supposed to be a geographic projection.
Second, I don't know why QGIS is misplacing the Landsat scene in the wrong place, or where in the process the scene was not transformed properly.
Join us at GIS SE, that´s the place for GIS related Q/A!
To help you out here:
Indeed, your crime was the CRS. The top level PROJCRS tag is the
key here, it reads out "WGS 84 / UTM zone 19N" from the data, with
the EPSG reference at the bottom (AUTHORITY["EPSG","32619"]]).EPSG:32619 is a UTM projected CRS based on the WGS84 geoid (datum) and with units in meter, defined as the projected distance to the corresponding reference meridians (Easting) and the equator (Northing). Since you defined the wrong CRS during import (i.e. EPSG:4326), the inherent coordinate values of the raster were treated as degree´s and the whole thing placed to the other end of the world. Run UpdateRasterSRID (SELECT UpdateRasterSRID(<shema_name>, <your_raster_table>, rast, 32619);) to set the raster's metadata to the correct CRS and reload the layer.
As for QGIS: it uses the CRS that you tell it to use. QGIS comes with a very handy on-the-fly reprojection feature (OTF, check out the general man page on 'working with projections' here) that lets you define an arbitrary CRS for your data to be projected and displayed (i.e. it reprojects the data's CRS into the defined one in memory, the data's metadata stays untouched).You can find the quick link button to the OTF settings in the bottom right corner of the GUI; set it to your desired SRID (e.g. 4326) (you´ll notice how the visual representation of your data changes according to the chosen projection. also the displayed coordinates will use the CRS units, e.g. decimal degrees for WGS84).

PostGIS ST_Expand point by meters and bounding box

I don't think something is right want max and min lat long of the 100 meter buffer.
SELECT ST_Asgeojson(
ST_Expand(
ST_GeomFromEWKT('SRID=4326;POINT(-88.33 36.33)')
,100
)
);
Think it is calculating by 100 degrees of lat and long.
This gis.se question shows what I'm wanting to do. Answer 5 graphically depicts what I'm wanting to do.
I'm trying to get the
coordinates: [
[-80.425, 46.437],
[-71.516, 46.437],
[-71.516, 37.936],
[-80.425, 37.936]`
to add it to the map.
Yes, of course... As in documentation of ST_Expand
Units are in the units of the spatial reference system in use denoted
by the SRID.
EPSG:4326 is in degrees so it's exanding by 100 degrees.
I suggest you use ST_Buffer function with geography datatype:
SELECT ST_Asgeojson(ST_Buffer((ST_GeomFromEWKT('SRID=4326;POINT(-88.33 36.33)'))::geography,100));
And if you need rectangle you have to add ST_Envelope with geography::geometry
SELECT ST_Asgeojson(ST_Envelope(ST_Buffer((ST_GeomFromEWKT('SRID=4326;POINT(-88.33 36.33)'))::geography,100)::geometry));
If you need to use this in select from table you might need to reproject your geometry because cast ::geography needs a LatLon CRS, so it'll look like this:
SELECT ST_Asgeojson(ST_Envelope(ST_Buffer((ST_Transform(x.the_geom,4326)::geography,100)::geometry));

I tried all ways, but still my area is calculated wrongly in Postgis

I created a very simple polygon in the middle of Germany to demonstrate my problem.
You can visualize it in geojsonlint using the following GeoJSON
{"type":"Polygon","coordinates":[[
[10.439844131469727,51.17460781257472],
[10.430574417114258,51.1753073564544],
[10.429565906524658,51.17179607723465],
[10.438792705535889,51.170706315523866],
[10.439372062683105,51.17267055874809],
[10.43975830078125,51.17439256616884],
[10.439844131469727,51.17460781257472]]]G}
When calculating the surface with online tools (e.g. http://www.daftlogic.com/projects-google-maps-area-calculator-tool.htm, but I tried several),
I get the following numbers (these are based on a similar drawing of the polygon, but not the exact same one, as I couldn't copy it over to these tools):
276583.39 m²
0.28 km²
68.35 acres
27.66 hectares
2977118.86 feet²
0.08 square nautical miles
Now I want to calculate these areas using POSTGIS, but I always get wrong and not matching numbers.
First I started without transformation using the examples given here:
http://postgis.net/docs/ST_Area.html
SELECT ST_Area(the_geom) As sqft, ST_Area(the_geom)*POWER(0.3048,2) As sqm
FROM (SELECT ST_GeomFromText('
POLYGON ((51.17460781257472 10.439844131469727,
51.1753073564544 10.430574417114258,
51.17179607723465 10.429565906524658,
51.170706315523866 10.438792705535889,
51.17267055874809 0.439372062683105,
51.17439256616884 10.43975830078125,
51.17460781257472 10.439844131469727))',4326) ) As foo(the_geom);
--> sqft = 3.52643124351653e-05 and sqm = 3.27616182873666e-06
How can I interprete these numbers?
Then I tried to transform it to WGS 84 / UTM zone 33N 32633
SELECT ST_Area(the_geom) As sqft, ST_Area(the_geom)*POWER(0.3048,2) As sqm
FROM (SELECT ST_Transform(ST_GeomFromText('
POLYGON ((51.174661624019286 10.440187454223633,
51.17067940750161 10.438899993896484,
51.17197097486416 10.429544448852539,
51.17536116708255 10.430488586425781,
51.174661624019286 10.440187454223633))',4326),32633) ) As foo(the_geom);
--> sqft = 662918.939349234 and sqm = 61587.1847391195
But even these numbers don't come close.
The coordinates of the polygon were accidentally loaded as lat,lon instead of lon, lat.
http://postgis.net/2013/08/18/tip_lon_lat
says
In spatial databases spatial coordinates are in x = longitude, and y = latitude
I converted the coordinates into EPSG: 31467, see epsg:31467 which is projected to meters and applies to the area of Germany covered by your geometry.
select st_area(st_transform(st_setsrid(st_geomfromtext('POLYGON((10.439844131469727
51.17460781257472,10.430574417114258 51.1753073564544,10.429565906524658
51.17179607723465,10.438792705535889 51.170706315523866, 10.439372062683105
51.17267055874809, 10.43975830078125 51.17439256616884, 10.439844131469727
51.17460781257472))'),4326),31467));
and got the answer: 274442.27 m2 which is within 0.007% of your original answer.
Measurements are usually more accurate in projected coordinate systems that use a geoid appropriate to that region. If you run this query on the spatial reference system table in Postgis for that projection:
select * from spatial_ref_sys where srid=31467;
you will see some more details, such as the fact that it uses the Bessel 1841 spheroid.
EDIT: your original geojson has coordinates in x/y, but for some reason you flipped them when putting them into Postgis.

Resources