How to make polygon from buffered point collection in postgis - postgis

I'm trying to make polygon of an airport which must be given distance from start point and end point of runway given as latitude and longitude.
Query I've made looks like this:
INSERT INTO airport (name, polygon)
VALUES(
'some airport name',
ST_SetSRID(
ST_Collect(
ST_Buffer(ST_MakePoint(160.04518, -9.43196), 100),
ST_Buffer(ST_MakePoint(160.06376, -9.42452), 100)
), 4326
)
);
unfortunately result polygon is very weird, it covers almost all earth. I've also tried to add srid to each point but it also didn't work. Any ideas?

Thanks to https://stackoverflow.com/a/13872887/4270929
before adding buffer to point it must be cast to geography (in geometry is in different unit I guess?)
ST_Buffer(ST_MakePoint(160.04518, -9.43196)::geography, 100)::geometry,
ST_Buffer(ST_MakePoint(160.06376, -9.42452)::geography, 100)::geometry

Related

Spatial Join in SQL Server

I come from a mining and exploration background. I have a list of points (with ID and X and Y coordinates).
Example
I have another table which contains polygons (claim numbers).
I am trying to find out which points fall within which polygons. My coordinates are in UTM Projection.
Thank you in advance!
I was trying the following code however my query is not returning results.
SELECT
pg.claim_number,
p.DHId
FROM leasehold as pg
JOIN
(SELECT
DHId,
X,
Y,
geometry::STPointFromText('POINT(',X,' ',Y,'), 0) AS [geom]
FROM collar
WHERE X is not null AND
Y is not null AND
claim_number is null) AS p
ON pg.Shape.STIntersects(p.geom) = 1
I was expecting to get a list with claim_number from polygon table which each DHId from point table intersects or falls within.
It looks like there just a syntax/quoting issue. I cleaned it up a bit and replaced STPointFromText with Point which is an MS-specific extension but doesn't require you to create WKT just to get a point. But that's really all I changed - I'd expect your general approach to work.
SELECT
pg.claim_number,
p.DHId
FROM leasehold as pg
JOIN (
SELECT
DHId,
X,
Y,
geometry::Point(X, Y, 0) AS [geom]
FROM collar
WHERE X is not null AND
Y is not null AND
claim_number is null
) AS p
ON pg.Shape.STIntersects(p.geom) = 1;
That said, I would expect this to not be super performant. You're creating the points on the fly and so will be incurring that cost at run time. As such, there's no way to put a spatial index on that data. If you can, I'd suggest adding a column to your collar table that is the geometry point and, as I implied, put a spatial index on it. Also, if there's not an index on the leasehold.Shape column, I'd put one there as well.

Converting geometry to json in PostGIS creates extra rows

I want to plot municipal borders in Apache Superset. Superset provides maps and provinces using the ISO-3166-2 standard, but I would like to plot municipalities as wel. There is a shape file with these borders and this is converted to a PostGIS table following instructions on this site. I used shp2pgsql to add the shape file to the PostGIS database. That resulted in a table containing some identifying informatie and a binary gemometry column.
The first row of the table is 231.375 characters long and looks like this (abbreviated):
"gid" "gm_code" "gm_naam" "shape_leng" "shape_area" "geom"
1 "GM0034" "Almere" 122665.358635 109562253.490 "0106000020E61000000300000001030000000100000017000000806CE7FB2F560241408B6CE73E441D41801C5A643A520...
This information is not readable by Superset so I converted it to geojson by this query:
CREATE TABLE gis.gemeente_2021_json AS
SELECT gm_code, gm_naam, shape_area,
json_build_object(
'type', 'Polygon',
'geometry', ST_AsGeoJSON(ST_Transform((ST_DUMP(geom)).
geom::geometry(Polygon, 4326), 4326))::json)::text as geojson
FROM gis.gemeente_2021_v1;
What happens is that I started with 435 municipalities but my new table has 1140 entries.
Some sample output is shown below. The lines are very long so I cut the lines and replaced this by ellipses (...). As you can see municipality "GM0034" has suddenly 3 rows with different entries in the gejson column while I expected just one row with a very long Geojson string.
gm_code gm_naam shape_area geojson
GM0034 Almere 109562253.49 {"type" : "Polygon", "geometry" : {"type":"Polygon","coordinates":[[[150213.998,479503.726],[150087.298999999,479382.379000001],[150000.420000002,479461.258000001],[150000.354600001,479461.317400001],[150000.366300002,479461.327300001],[150001.45630,...]]]}}
GM0034 Almere 109562253.49 {"type" : "Polygon", "geometry" : {"type":"Polygon","coordinates":[[[141872.969,483192.398800001],[141872.978100002,483192.398499999],[141872.984099999,483192.398499999],[141904.523899999,483191.793400001],[141912.174600001,483191.646699998],[141912.340999998, ...
GM0034 Almere 109562253.49 {"type" : "Polygon", "geometry" : {"type":"Polygon","coordinates":[[[144312.481800001,492971.460499998],[144312.557,492971.443999998],[144312.633299999,492971.445799999],[144316.953000002,492971.539999999],[144321.4701,492972.263300002],[144321.730999999,492972.305], ...
The data may be Multipolygon though the documentation mentions Polygon. I tried that but that basically yielded the same results.
Checked the Postgres documentation on maximum linelength but that is about 1GB of text per occurrence and although the lines are long, 250K can be easily handled by postgres.
Any suggestion to where I should look further is welcome.
Edit 1
As #JGH rightfully pointed out I'd made quite a mistake in the SRID's. The shape file's SRID is 28992. I deleted the table, created it anew with the correct SRID. It displayed correctly on openstreetmap. It is converted as follows:
CREATE TABLE gis.gemeente_2021_json AS
SELECT gm_code, gm_naam, shape_area,
json_build_object(
'type', 'Polygon',
'geometry', ST_AsGeoJSON(ST_Transform((ST_DUMP(geom)).
geom::geometry(Polygon, 28992), 4326))::json)::text as geojson
FROM gis.gemeente_2021_v1;
The same error applies, alas. Still 3 rows for GM0034.
Edit 2
I adjusted the query according to the suggestions of #JGH:
CREATE TABLE gis.gemeente_2021_json AS
SELECT gm_code, gm_naam, shape_area,
json_build_object(
'type', 'MultiPolygon',
'geometry', ST_AsGeoJSON(ST_Transform(
geom::geometry(MultiPolygon, 28992), 4326))::json)::text as geojson
FROM gis.gemeente_2021_v1;
and that worked.
The data show coordinates like [150213.998,479503.726], yet the code contains the line geom::geometry(Polygon, 4326) followed by a useless transform to 4326. The shown coordinates are not in 4326, they have probably been loaded without a defined CRS (so 0 is assigned and the cast works).
The data is therefore declared to be in lat-long 4326 but contains values for another CRS, you end up with artistic coordinates which can't be properly handled (maybe it goes several times around the earth, maybe it goes to the pole and back etc... anything can happen). Garbage in, garbage out.
So the first step is to set the proper CRS. Then you can likely apply a -usefull- transform from this other CRS to 4326. If you want single parts, keep using the dump and handle the attributes repetions. If you want multi-parts, remove the dump and set the geojson type to MultiPolygon.

Find geometries with coordinates that do not belong in projection

I was recently supplied data in a shape file projection EPSG:4326. I imported this into my PostGIS database and then tried to transform to 3857. I got the error
ERROR: transform: couldn't project point (-99762.4 -2.87277e+006 0): latitude or longitude exceeded limits (-14)
Even after applying st_force2d and st_makevalid I still couldn't transform until I managed to track down a delete the one geometry that was "out of bounds" for Lat/Lng.
My question is: how do I easily find geometries that don't fit in the current projections envelope?
In case of 4326 this should work:
SELECT * FROM your_table WHERE
(st_x(geom) NOT BETWEEN -180 AND 180)
OR
(st_y(geom) NOT BETWEEN -90 AND 90) ;
(I would have left this as a comment, but since I can't, here it is.)
This answer could be helpful:
Get projection limits from Postgis
Basically:
PostGIS doesn't know about a projection's bounds and you would need to make a new table to collect it.
Even if the value is contained in a projection's limits, it doesn't mean that it is right for this projection. This method can only ensure that the transform process will proceed and get you a result, it can not ensure that the data is correct.
For a more generic solution, you could use a custom transform function which catch the error:
CREATE OR REPLACE FUNCTION transform_safe(geom geometry, srid int) RETURNS geometry AS $$
BEGIN
IF ST_Srid(geom) = 0 THEN
RAISE Exception 'Input geometry has unknown (0) SRID';
END IF;
BEGIN
RETURN ST_Transform(geom, srid);
EXCEPTION WHEN internal_error THEN
RAISE WARNING '%: %',SQLSTATE,SQLERRM;
END;
RETURN NULL;
END;
$$
language plpgsql;
This behaves the same as ST_Transform but returns NULL if the coordinates are out of bounds.
In example, so could you identify the records with invalid coordinates:
SELECT id FROM polygon_table WHERE transform_safe(geom) IS NULL AND geom IS NOT NULL;

Finding polygons within a certain radius of a number of other polygons

I have a table with a bunch of polygons (or multipolygons, I'm not sure...does it matter?) of one type (A) defined in a CTE, and then another of another type (B) in another CTE. I want to filter for just type A polygons that are within a given radius of any of the polygons of type B. How do I do this?
Create a collection of your 'B' polygons using ST_Collect & then use a WHERE clause with ST_DWithin to specify your distance parameter.
For example:
WITH polys_a AS (
SELECT geom
FROM buildings_dc
),
polys_b AS (
SELECT geom
FROM buildings_va
)
SELECT polys_a.*
FROM polys_a,
(
SELECT ST_Collect(geom) as geoms
FROM polys_b
) as c
WHERE ST_DWithin(a.geom, c.geoms, .001);
Note that both sets of geometries may be of different types (e.g. Polygon, Point, MultiPolygon, etc.), but they must be of the same projection/ coordinate system. If you are using standard WGS84 (SRID 4326), the distance parameter is in terms of degrees.

Postgis / Elixir Geo. Location results are correct. But longitude is getting changed

I'm storing this value %Geo.Point{coordinates: {10.78639, 106.70095}, srid: nil}, but when I query (the result is correct distance-wise), the the returned value has a different lng. %Geo.Point{coordinates: {10.78639, 73.29905}, srid: 4326}.
The original is near Saigon, Vietnam. The new one is off the west coast of India.
It appears PostGIS points are (longitude, latitude) not (latitude, longitude).
Thanks ambiguous documentation: PostGIS: Is it lon/lat or lat/lon? a simple "It's Long,Lat" would be helpful here.

Resources