I downloaded OpenStreetMap data and uploaded to a local PostgreSQL database with osm2pgsql.
I created a new table as a result of a ST_Union function with only one record (for now).
CREATE TABLE IF NOT EXISTS m_temp.budapest_merge
(
id text COLLATE pg_catalog."default" NOT NULL,
st_union geometry(Polygon,3857),
ter double precision,
CONSTRAINT budapest_merge_pkey PRIMARY KEY (id)
)
Then I checked the area of my polygon.
SELECT ST_Area(st_union) FROM m_temp.budapest_merge
The result was 1151156216.758265 sqm which was weird for me, because I was expecting something around 520 sqkm.
I opened the PostgreSQL layer in QGIS, calculated the area with the Field Calculator tool's $area function.
The result was 526031443.36087507 sqm, which corresponds to my expectation and reality as well.
My question is what can be a problem here?
The QGIS project is in the same projection (EPSG:3857).
Also I know that naming a geometry column st_union is not really nice, but that's not the main concern here.
In Qgis, $area is (if defined), the ellipsoidal area, i.e. the true ground area, while area(geometry) is the planar area, i.e. highly impacted by the geometry projection.
In Postgis, st_area always use the geometry projection, so it is the equialent of QGIS area(geometry).
That being said, 3857 highly distorts distances and areas as you move away from the equator. It is wrong to use this projection for computing distances or areas.
Related
I'm currently storing a lot of data on a Azure SQL for Postgres instance. My table consists of columns like lat, lon, and geometry which is created using PostGis function
ST_SetSRID(ST_MakePoint(lat,lon),4326))
However, when I import the table into QGIS using a filter on an identifier column and between two timestamps the QGIS Query Builder clearly shows a decent number of records being returned. However, QGIS (I've tried Google Map, OpenStreetMap) renders the Geometry points near Africa (see screenshot below). Looking at some lat and lon pairs on maps.google.com that make up the geometry points, the location is completely different from what QGIS shows.
I've changed the CRS of the project, the Layers for the Map and the Coordinates (i.e. geometry points) to 4326. But still all the data points show up near Africa.
Any ideas what may be causing this and how to resolve it?
PostGIS convention is to pass longitude first (as X coordinate) and latitude second (as Y). So try
ST_MakePoint(lon, lat, 4326)
I try to figure out how to come from a single given coordinate (lat/lon) to the nearest bounds which enclose this coordinate on a map e.g. streets or sea.
Here two examples to give you a better understanding of what I mean:
What i tried already or thought about:
Setting up a Nominatim server and search for the given coordinate via the reverse-function to get the bbox and/or the geojson polygon of this coordinate. -> this only works when the given coordinate is within a POI or for example directly on a street.
Writing an algorithm to walk in all 4 or 8 directions (n/e/s/w) and 'stop' when the map layer/surface changes (change = stop for this direction and mark a bounding-point)
Building up an image-recognition system using TensorFlow to detect the different colors and 'draw' the polygon. Worked with TensorFlow a couple of times but this seems to be the most tricky solution to implement (but at my current understanding the most precise one)
Does someone of you have any other ideas to get a solution for this problem? Would appreciate any kind of approaches
Cheers!
If I got your question right, you might wanna first select all polygons in which the given point is inside of using ST_Contains, and then compute the distance to this point using ST_Distance. If you ORDER BY distance and LIMIT to 1 result you'll get the nearest polygon, e.g.
Data Sample
CREATE TABLE t (gid int, geom geometry);
INSERT INTO t VALUES
(1,'POLYGON((-4.47 54.26,-4.44 54.28,-4.41 54.24,-4.46 54.23,-4.47 54.26))'),
(2,'POLYGON((-4.48 54.25,-4.40 54.25,-4.41 54.23,-4.48 54.23,-4.48 54.25))'),
(3,'POLYGON((-4.53 54.23,-4.44 54.29,-4.38 54.22,-4.53 54.23))');
Query
SELECT gid,ST_AsText(geom) FROM t
WHERE ST_Contains(geom,ST_MakePoint(-4.45, 54.25))
ORDER BY ST_Distance(geom,ST_MakePoint(-4.45, 54.25))
LIMIT 1;
gid | st_astext
-----+------------------------------------------------------------------------
1 | POLYGON((-4.47 54.26,-4.44 54.28,-4.41 54.24,-4.46 54.23,-4.47 54.26))
(1 Zeile)
I am new to PostGIS, am not getting the area of polygon right, my sample data is from Google maps, I know the area of the polygon is 11 acres, but the area returned by st_area doesn't match,
I already referred to a few links like below, but unable to resolve the issue, Internet says google follows 4326 Spatial references, I tried a lot, can you please help, Image attached is the polygon from google maps.
I am expecting an array of such coordinates from the user, I have to calculate the area from PostGIS and give an error back to the user if the area entered is not approximated to calculated area.
https://gis.stackexchange.com/questions/169422/how-does-st-area-in-postgis-work
How do I convert a latitude/longitude pair into a PostGIS geography type?
https://gis.stackexchange.com/questions/56862/what-spatial-reference-system-do-i-store-google-maps-lat-lng-in/56925
17.475197 78.389024
17.4771 78.39044
17.475657 78.391652
17.474408 78.390847
17.475197 78.389024
l_polygon_text='MULTIPOLYGON(((
17.4771000000000001 78.3904399999999981,
17.4751970000000014 78.3890240000000063,
17.4756570000000018 78.3916519999999934,
17.4751970000000014 78.3890240000000063,
17.4744080000000004 78.3908469999999937,
17.4771000000000001 78.3904399999999981)))';
st_area(ST_GeometryFromText(l_polygon_text,4326))
st_area(ST_GeometryFromText(l_polygon_text,2163));
st_area(ST_GeometryFromText(l_polygon_text,2249));
st_area(ST_GeometryFromText(l_polygon_text,3859));
ST_AREA(ST_Transform(ST_GeomFromText(l_polygon_text,4326),31467));
ST_Area(ST_Transform(ST_SetSRID(ST_GeomFromText(l_polygon_text),4326),900913));
polygon
In PostGIS, coordinates must be expressed as longitude first, then latitude. Google uses the opposite.
After swapping the coordinates to the proper order, you can't directly call st_area, else you would get an area in "square degrees" which is meaningless. You would have to project to a suitable local coordinate system, or you can use the geography type which will return an area in m2.
select st_area(st_geogFromText('MULTIPOLYGON(((78.3904399999999981 17.4771000000000001, 78.3890240000000063 17.4751970000000014,78.3916519999999934 17.4756570000000018,78.3890240000000063 17.4751970000000014,78.3908469999999937 17.4744080000000004,78.3904399999999981 17.4771000000000001)))'));
st_area
--------------------
26956.897848576307
That being said, the example you have provided is about 6.5 acres, not 11, because the polygon is not properly defined:
I'm using postgresql as db , i have the table named car_wash with field "point geometry"(use postgis) so in application I'm getting lon lat from user using GOOGLE API, next step I need to create circle around user and check if car_wash inside this circle I use
select *
from car_wash cw
where
ST_DWithin (
cw.lon_lat,
ST_GeomFromText('POINT(54.21 22.54)')
)=false
AND
not cw.was_deleted
Is it corect way? IF you need my srid is 0 according to this query
Select Find_SRID('public', 'car_wash', 'lon_lat')
First - i assume that lat_long is georaphy type column. If it is geometry type column you will have to modify my examples to some other EPSG (propably 3857 metric EPSG for whole world). It is very important because st_dwithin check in meters for geopraphy type , and in map units for geometry (and for EPSG 4326 unit is degree not meter)
Insert your data like this
insert into car_wash values (1,'aaa',st_setsrid(st_makepoint(54.51, 22.54),4326))
I explain why use st_setsrid, st_makepoint, and what the hell is 4326.
- 4326 is EPSG 4326 - it is most known coordinate reference system (where you have lat and long in degrees).
st_makepoint - will create geography point from your lat and long coordinates. It will looks like bytes, but dont worry, if you will need lat and long for some reasons you can get them with st_x() and st_y() or st_astext() functions. Best thing of have geoms or geogs (in this case) is that you can use gist index. Very powerful tool that speed up your geo queries.
st_setsrid - st_makepoint will create point but with srid=0. You have to tell POSTGIS in what EPSG it should read your data. For example if you tell him to read it with 4326 it will be in correct places on google world map, but if you say for example 3857 it will be in completly diffrent place, as 3857 is metric system not degree so it will be around 50 and 50 meters from left down corner (or maybe left up, dont remember)
Create index on geog
create index on car_wash using gist (geog);
We have table, we have data in it and index on it. Now we want to check if your point is close to any of your car washes.
select *
from car_wash cw
where ST_DWithin (cw.geog,ST_GeogFromtext('SRID=4326;POINT(54.21 22.54)'),1000)
AND cw.was_deleted=false
In ST_DWithin third parameters is distance in meters (georpahy) or map units (geometry). So in this case it will show you all car washes that are up to 1000 meters from your user location and are not deleted.
While using ST_DWithin function, your third parameter must be distance.
You can also define srid in ST_GeomFromText
there are two simple example so you can see difference:
select ST_DWithin(
st_geomfromtext('POINT(54.51 22.54)',4326),
st_geomfromtext('POINT(54.21 22.54)',4326),0.5
)
result is true
select ST_DWithin(
st_geomfromtext('POINT(54.51 22.54)',4326),
st_geomfromtext('POINT(54.21 22.54)',4326),0.1
)
result is false
Source:
https://postgis.net/docs/ST_DWithin.html
https://postgis.net/docs/ST_GeomFromText.html
SRID of the ST_GeomFromText('POINT(54.21 22.54)') must be same as the SRID of cw.lon_lat. Suppose SRID of cw.lon_lat is 4326 you can set the other attribute srib by using ST_GeomFromText('POINT(54.21 22.54)',4326).
Secondly, ST_DWithin needs buffer distance as 3rd parameter. So suppose if you want to check if point is within 100 meter buffer it should be like
ST_DWithin (
cw.lon_lat,
ST_GeomFromText('POINT(54.21 22.54)', 3857), 100
)
Buffer value is according to the srid unit. in case of 3857 its meter. You need to convert cw.lon_lat and POINT(54.21 22.54) to the same SRID in order to make this work, using st_setSRID e.g.
I'm pretty new to postgresql, so there might be a pretty simple answer to my question, at least I hope so.
I have imported a table with thousands of single linestrings that represent the main roads of a country.
I'd like to buffer every single one of them and intersect the results with another polygon (Basically just a circle, but the thing is, that the position of the circle is dynamic, depending on the preferences of the user).
However, I don't know how to buffer all linestrings at once. It works just fine when I buffer and intersect just one linestring, but it's kinda crucial that I buffer all of them.
And importing the roads as a multilinestring with SPIT doesn't work at all.
So ... how do I make that happen? Any hints?
I'd really appreciate any help.
The best approach would be to simply add another column that represents the buffers of the roads and add a spatial index, ie,
alter table roads add column road_buffer geometry(POLYGON, SRID);
update table roads set road_buffer = st_buffer(roads, distance);
create index ix_spatial_road_buffer on roads using gist(road_buffer);
where POLYGON and SRID indicate the type and spatial reference ID of the column. You can omit this, although it is good practice to use a specific type and SRID. YOU could also use AddGeometryColumn for the same end.
You can now run a query against the buffered roads, which will be fast, as it is indexed, but return the actual roads, eg,
Select road_id, road from roads where st_intersects(road_buffer, circle);
Now, if you wanted to do it the other way, without actually having a pre-buffered linestring, you could do somthing like,
select road_id, road, road_buffer from
(select st_buffer(road, dist) as road_buffer, road, road_id
from roads where
st_intersects(st_expand(st_envelope(road), dist), circle)
) road_buff
where st_intersects(road_buffer, circle);
The trick here is that you compute the buffer in a subquery, but only for those linestrings/roads whose envelope (ie, minimum bounding rectangle) intersects with your circle -- a much faster calculation than buffering all linestrings. Note, also, the use of st_expand, by the same amount as the buffer distance, which basically expands the mbr and ensures you don't miss any potential candidates. I did a quick test on half a million random lines, and this approach was much faster (10x) than simply checking for circle intersections against the buffer of all points.
Actually I came up with a solution that works just fine.
I just buffered my linestrings externally (used qgis) and reuploaded the entire thing as one big polygon.
However, I'd still like to know how it is done while maintaining the linestring structure.
Both approaches that John Barça suggested would work for me.