Unions of polygons (ST_UNION for Geography type) - postgresql

I am looking for a function that will return the intersection of 2 or more polygons (geography type).
I am aware of ST_UNION, ST_COLLECT but it works only for geometry type.
Any tip will be really appreciated

You can cast to geometry and carry out the operation there. You will just have to be careful that your shapes makes sense when evaluated on a cartesian plane. Do they wrap the dateline or poles?
select geography(st_union(a::geometry, b::geometry))
If the shapes have very long edges, then the difference in edge interpolation between the great circle interpolation you want on a sphere and the linear interpolation you get on a plane comes into play, and you have to get fancier to preserve the edge shapes as best you can by working in an appropriate map projection (automatically chosen with the bestsrid function).
select geography(
st_transform(
st_union(
st_transform(a::geometry, _st_bestsrid(a,b)),
st_transform(b::geometry, _st_bestsrid(a,b))
),
4326
))
Enjoy!

The Geography type supports only a small subset of the PostGIS functions. Here you can check them all and see if any of them suits your needs:
http://postgis.refractions.net/docs/ch08.html#PostGIS_GeographyFunctions

Have you considered a ST_Transform into a geometry type nested within the ST_Union or ST_Collect? PostGIS docs (from amercader's link) say they use that function internally for some geography operations.

Related

Strange difference with ST_DWithin - Geography vs Geometry

(EDIT: Sorry. Forgot to mention. Postgresql 13, PostGIS: 3.1 USE_GEOS=1 USE_PROJ=1 USE_STATS=1. Sorry about that).
The following 2 queries, in my opinion, should return "true" in both cases.
The point is clearly within the polygon (square). Displaying both certainly indicates so.
I am starting to wonder what fondamental thing I am missing here.
To be honest, I have been circling around this issue for years, as I am never able to find something "final" to understand what is happening. In this case, the point is somewhat near the edge of the polygon, but from time to time I had the same issues with points that were well within the polygon.
The only difference between the 2 queries is that one uses geometry the other uses geography. As you can see, we use 0.0 for the distance value. This is like an intersects.
OH, and by the way, using intersects yields the same results. I am very confused about this.
select
ST_Dwithin(
St_GeometryFromText(
'POLYGON((-95.979486 41.676556,-81.432269 41.676556,-81.432269 30.196043,-95.979486 30.196043,-95.979486 41.676556))', 4326),
ST_GeometryFromText('POINT (-87.70551 30.24481)', 4326),
0
)
select
ST_Dwithin(
St_GeographyFromText(
'POLYGON((-95.979486 41.676556,-81.432269 41.676556,-81.432269 30.196043,-95.979486 30.196043,-95.979486 41.676556))'),
St_GeographyFromText('POINT (-87.70551 30.24481)'),
0
)
EDIT.
Sorry, not sure of the right etiquette. Following the great responses, I am adding some more context, in case somebody finds it interesting.
I am working on a "world" dataset containing country polygons. Since some of these are huge, I have elected to st_subdivide them.
![st_subdivide usa] https://imgur.com/IQRz8vg
And when zooming we can see EXACTLY what is explained in the answer below:
![zoom st_subdivide usa] https://imgur.com/fgmpBx0
So wow, perfect explanation, thank you very much.
I know that I should not use geography, well, "should not". However, I also need to work a lot with distances and expansion, and geom(4326) is quite annoying with units I find.
First of all, you need to understand the difference between geometry and geography.
Geometry, where it assumes all of your data lives on a Cartesian plane.
Geography, where it assumes that your data is made up of points on the earth's surface.
The problem is the following. When you are projecting the geometry, PostGIS is assuming that the edges of the geometry are not planar (since the earth's surface is not planar) and transforming them to spherical (this process is called tessellation). Therefore, when you try to intersect them the point is not inside de polygon.
If the geographies are small enough, the difference between spherical and planar edges can be ignored, but in your case, the polygon is a huge one.
You are projecting your geometry on the EPSG 4326 in order to project it on the earth's surface, however although the point is inside the square in a planar coordinate system, it is not in a spherical one.
To sum up, you need to know what type, geometry or geography, works depending on your use case.
You can solve this issue by forcing the edges to be planar (in some implementations of PostGIS as BigQuery):
St_GeogFromText(
'POLYGON((-95.979486 41.676556,-81.432269 41.676556,-81.432269 30.196043,-95.979486 30.196043,-95.979486 41.676556))',
planar => True)
In addition to the answer from #sergiomahi - which should be accepted - I would like to add a few examples to make his point clearer. The issue isn't the type geography itself, but in which surface the features are projected.
Given your polygon and point, if we ST_Distance using geometry the distance is zero, since it will use a Cartesian plane, but using geography it returns 16524.35577956 metres due to the spherical edges and the size of your polygon, as pointed out by #sergiomahi. So, they no longer overlap.
WITH j (pol,poi) AS (
VALUES
('SRID=4326;POLYGON((-95.979486 41.676556,-81.432269 41.676556,-81.432269 30.196043,-95.979486 30.196043,-95.979486 41.676556))',
'SRID=4326;POINT (-87.70551 30.24481)')
)
SELECT
ST_Distance(poi::geometry,pol::geometry) AS geom_dist,
ST_Distance(poi::geography,pol::geography) AS geog_dist
FROM j;
geom_dist | geog_dist
-----------+----------------
0 | 16524.35577956
However, if you stick to geometry and tell PostGIS that the distance is supposed to be calculated using a sphere you get similar results as with geography:
WITH j (pol,poi) AS (
VALUES
('SRID=4326;POLYGON((-95.979486 41.676556,-81.432269 41.676556,-81.432269 30.196043,-95.979486 30.196043,-95.979486 41.676556))',
'SRID=4326;POINT(-87.70551 30.24481)'))
SELECT
ST_Distance(poi::geography,pol::geography) AS geog_dist,
ST_DistanceSphere(poi::geometry,pol::geometry) AS geom_dist_sphere
FROM j;
geog_dist | geom_dist_sphere
----------------+------------------
16524.35577956 | 16574.61755546
(1 Zeile)

Calculate distance between two coordinates using postgres?

I´ve got a job offer to work with postgres and I have not much idea of it. The guy told me to build a simple data base which automatically calculates the distance to my house from a list of some other places (bars, pharmacies, museums, whatever...) everything given in geocoordinates.
I have already installed postgres, also postgis and create a data base. May you give me some hints about how I should do this task? Is there any tutorial or resource I could use to make this tasks easier? Should I use postgis?
Thank you.
PostGIS will do this easily. Boundless Geo have an excellent PostGIS tutorial. I also recommend you are familiar with Ch3 & 4 of the PostGIS manual.
I strongly advise you to learn & understand the difference between projected and unprojected coordinates if you're going to be working with spatial data. Projected means the coordinates have been taken from a 'round earth' and adjusted or projected onto to a flat map page (with coordinates normally given in feet or metres depending on the properties of the projection used). This enables computationally efficient normal cartesian calculations to be done for distance, area, direction, intersection, contains etc. There are trade offs for using projections- You can't preserve all of area, distance, shape, direction when you project a curved line or surface onto a flat map. Different projections are optimised for different trade-offs. Calculations on projected data are only accurate over a relatively small portion of the earth's surface. There are many map projections available to suit various needs and localities. If you are going to be working with projected data, you need to pick a projection that suits your purposes and location. If you don't understand projections, your queries can easily produce garbage without realising it.
Unprojected data (ie, raw Lat/Lon coordinates which is what you have) involve much more complicated calculations as they are done on the curved surface of the spheroid representing the earth. There are a number of reference coordinate systems that are used to express Lat/Lon, however the most common is "WGS84" (which is what "GPS" coordinates are expressed in).
PostGIS objects (in the form of "simple features" as defined by the OGC) can be stored as either "geometry" types (projected coordinates) or "geography" types (unprojected Lon/Lat in WGS84: note the order, a common source of confusion!). As a bit of a wrinkle, Lon/Lat (order again!) can also be stored as a "pseudo projected" geometry type (typically with a projection SRID of '4326' for WGS84 Lon/Lat).
The method you use to calculate distance will depend on how you choose to store your points ('geometry' or 'geography').
See ST_Distance from the PostGIS docs for excellent examples of measuring distance using both geometry and geography points. Note, if you wish to calculate projected map distances you will need to pick an appropriate map projection and use ST_Transform to project your points to the appropriate spatial reference system (currently in SRID 4326- "GPS" coordinates). For only a few points the difference won't be at all noticeable, but once you start doing lots of more complex spatial queries, the difference can be significant. PostGIS has a lot more functions for geometry types than for geography types which may influence your decision. Also see ST_DistanceSpheroid for another possibility for calculating distance from Lon/Lat coordinates.
To start with, I'd store your points as 'geography' to simplify your experiments. Your distances will then be 'great circle' calcs in metres and you won't have to worry about projections initially.

Creating Postgis Polygon using ST_ConcaveHull from Geometry Points

I have a postgis 2.2 table with 20 columns of type geometry(Point,4326)
I'd like to generate a polygon which covers the outer boundary of the points - it seems like ST_ConcaveHull is a good option, but I can't see how to do it without first converting my points back to text (which seems to be missing the point).
Is st_concavehull the right option, and how do I go about constructing the query?
Thanks!
You first need to collect your points, then pass this collection to ST_ConcaveHull:
ST_ConcaveHull(ST_Collect(geom), 1)
Per the ST_ConcaveHull documentation:
Although it is not an aggregate - you can use it in conjunction with ST_Collect or ST_Union to get the concave hull of a set of points/linestring/polygons ST_ConcaveHull(ST_Collect(somepointfield), 0.80).

PostGIS, Is possible to create a Polygon from some Points

Well,
I used to create Points or Polygons from latitude and longitud. Using something like that:
ST_Transform(ST_GeomFromText('POINT("+longitud+" "+latitud+")' ,4326),23030)
But on that moment my source are geometry values of thousands of points, and I would like to create a geometry Polygon value from them. But I don´t know if it is possible.
Also I have UTM coordinates(mine is 30 S), but I think that starting from geometry would be easier.
If someone could help me, I would be so gratefull.
Thanks
You can collect an array of point, cast to geometry[], and use ST_MakeLine.
SELECT ST_MakePolygon(ST_MakeLine( ARRAY[ point(1,2), point(3,4), point(5,6) ]::geometry[] ));

Geoserver - How do I draw a geodesic line that represents the great circle between two points

I'm using Geoserver version 2.1.1, Postgres 9 and PostGIS 2.0
What I want to achieve should (i think!) be quite straight forward. I want to render on a map a line that represents the Great Circle between two cities on the earths surface.
My database contains the city locations represented as geography points defined as latitude and lonfitude pairs.
I have a layer defining an SQL view in Geoserver which retrieves a linestring (st_makeline) from the two coordinates for the specified cities. I'm having to type cast the geographies to geometries to get this to work.
But when I draw the returned line on a map what i get is a straight line and not the curved line that I am expecting.
Can someone tell me how I should be going about this?
Thanks!
PostGIS offers mainly "constructors" of the base geometries point, linestring and polygone, like ST_MakeLine.
And what yo uwant to do depends also on the coordinate reference system you use when displaying your map layers.
Here's a nice trick about great circles or parts of:
https://gis.stackexchange.com/questions/5204/curved-point-to-point-route-maps
Yours, Stefan
P.S. Here's some related stuff:
Drawing circles on a sphere
And here's some math:
http://www.mathworks.ch/matlabcentral/newsreader/view_thread/277881
I had a similar problem in cartodb (which also uses PostGIS); I wanted to get curved lines from straight lines. Maybe this post can help.