postgis : select points and polygons of one table inside polygon of another table - postgis

I have searched the forum and tried with solutions of the forum but it didnt work.
I have tried to select points and polygons of one table inside a polygon of another table with the following code :
SELECT pt_poly.*, poly2.*
FROM osm_fuel pt_poly
JOIN boundingbox poly2
ON ST_Intersects(poly2.way, ST_GeometryFromText(pt_poly.geometry,27572));
I have also tried with st_contains but it's the same.
I end up with a table of rows which geometry is only my poly2 polygon repeated many times.
I remember managing to do this with a join and a. ::geometry but i couldnt find exactly how. If someone could help me with this it would be great.
by the way, what is the meaning of :: in postgis?

Most likely you open the result in some app (e.g. QGIS) and because in this query only one column has type 'geometry'(poly2.way) you see the described result.
Don't use *, specify the columns you want explicitly, and append to result columns transformation wkt-geometry from pt_poly, e.g.:
SELECT pt_poly.<column_name1>
, pt_poly.<column_name2>
, poly2.<column_name1>
, poly2.<column_name2>
, ST_GeometryFromText(pt_poly.geometry,27572)) as geom
FROM osm_fuel pt_poly
JOIN boundingbox poly2
ON ST_Intersects
( poly2.way
, ST_GeometryFromText(pt_poly.geometry, 27572)
)

Related

SQL group searching and matching between two tables

Working on a physical security migration. Have two tables. First table (AreaAccess) lists the badgeholder with the areaid's the badgeholder has access to. Second table (AreaGroups) has areaid's grouped together in sets. The goal is to read the cardholder's AreaAccess records and then search the AreaGroups for the count of the best or exact match of the cardholder's areas to a group.
Curious why you didn't give this a shot first. If you aren't as familiar with SQL, here's a great link to get started: https://www.w3schools.com/sql/
Also, it's extremely helpful if you can provide a sample of what you want the outcome to look like. It doesn't have to be fancy, just a few rows/columns that can demonstrate what you're hoping to see.
Here's a possible look. However, your question is a little vague, so this is a best guess.
create table ##CardToArea --Is this table a log, or is it grouping of permission? I'm treating it as a log, but the wording of your question isn't quite clear.
(
CardholderID int not null
, AreaID int not null
);
insert into ##CardToArea
(CardholderID,AreaID)
values
(1961,11)
,(1961,25)
,(1961,28)
,(1961,71)
,(1961,73)
,(1961,74)
,(1961,44)
,(1961,50)
,(1961,51)
,(1961,52);
create table ##AreaToGroup
(
AreaID int not null
, AreaGroupID int not null
, unique (AreaID,AreaGroupID)
);
insert into ##AreaToGroup
(AreaID,AreaGroupID)
values
(33,0)
,(45,0)
,(45,7)
,(19,16)
,(17,16)
,(11,16)
,(11,48)
,(17,48)
,(17,49)
,(15,49)
,(11,49);
select
isnull(convert(nvarchar,atg.AreaGroupID),'Not defined') as [AreaGroupID]
, cta.CardholderID
, count(*) as [CountOfAccesses]
from ##CardToArea as cta
left join ##AreaToGroup as atg on cta.AreaID = atg.AreaID
group by
atg.AreaGroupID
, cta.CardholderID;
drop table ##AreaToGroup;
drop table ##CardToArea;
#Robert - thanks for the response.
This is my query I had worked through yesterday. Looking only at two sample users (23006 and 28190). The result is a full report of the areasets these users are part of. What I have been trying to do, hence yesterdays question, is to limit the query to the top five areacounts for each cardholder. Attempted to use ROW_NUMBER processing but that was not working primarily because of the alias for "count(g.areaid)" in the select. I also tried numerous sub queries but to no avail.
select g.AreaGroupID, ag.caption, count(g.areaid) as AreaCount, a.CardholderID
from AHBadgeActivity B
join areaaccess a on a.CardholderID=b.CardholderID
left join AreaGroupSet g
on g.areaid=a.AreaID
left join AreaGroup ag on AG.AreaGroupID=g.AreaGroupID
where (a.CardholderID=23006 or a.CardholderID=28190) and DeleteFlag=0 and g.AreaGroupID <> 0
group by g.AreaGroupID, ag.Caption, a.CardholderID
order by a.cardholderid, AreaCount desc
Here is the sample output. My goal is to limit to the top five AreaCounts for each Cardholder.
Output from Query

SSRS multi axis chart issue

I was wondering if anyone can help me with this issue I have been having for at least a good 10 hours now after playing with it. I am going to use the Movies database from wiseowl to illustrate this instead.
I have the following SQL:
SELECT FilmReleaseDate
,FilmName
,directorname
,FilmRunTimeMinutes
,directorgender
,CountryName
,datename(M, FilmReleaseDate) AS [month]
,month(FilmReleaseDate) AS [month_no]
,year(FilmReleaseDate) AS [Year]
FROM tblFilm
INNER JOIN tblDirector ON directorID = FilmDirectorID
INNER JOIN tblCountry ON FilmCountryID = CountryID
WHERE FilmReleaseDate >= '2006-01-01'
now say I want to put this into a stacked chart with the movies from each country, the second axis will show the running minutes of the films. But the problem here is that I want the average for all countries, not for each one. Is there any way to do this so I have 1 line rather then 3 lines.
here is how the out put looks like
as you can see from output there are 3 lines for the 3 countries in the result, is there a way to get an average for all of them in one line rather then each one so the output will have only one line showing the average.
If anyone can help I will really appreciate it as I have spent countless hours on this. Any other info you need let me know.
Try this script. Then use the new field D.AVGFilmRunTimeMinutes on your chart. I added aliases on each table on the script. Just correct the aliases if they're pointing to a wrong table.
SELECT A.FilmReleaseDate
,A.FilmName
,B.directorname
,D.AVGFilmRunTimeMinutes
,B.directorgender
,C.CountryName
,datename(M, A.FilmReleaseDate) AS [month]
,month(A.FilmReleaseDate) AS [month_no]
,year(A.FilmReleaseDate) AS [Year]
FROM tblFilm A
INNER JOIN tblDirector B ON directorID = A.FilmDirectorID
INNER JOIN tblCountry C ON A.FilmCountryID = B.CountryID
LEFT JOIN (SELECT AVG(FilmRunTimeMinutes) AS AVGFilmRunTimeMinutes,FilmDirectorID FROM tblFilm GROUP BY FilmDirectorID) D
ON D.FilmDirectorID = A.FilmDirectorID
WHERE A.FilmReleaseDate >= '2006-01-01'
The reason your chart is producing 3 lines instead of one, is due to the grouping in you chart for each month (determined by how many countries).
I don't know the structure of your table, so I would recommend adding another field to your dataset:
YEAR(A.FilmReleaseDate) * 100 + MONTH(A.FilmReleaseDate) AS [YearMonth]
This will add a field with the format YYYYMM
Add this to your chart on your axis instead year and month_no.
In the Chart Data window, click the drop down next to the newly created Category Group for Year Month. Select Chart Group Properties, and copy the name. (It should be called something like - Chart1_CategoryGroup)
The final step is to modify the expression for your Film Run Time in the chart.
Click on the drop down next to FilmRunTimeMinutes, and click Series Properties.
In the value field use the following:
=Avg(Fields!FilmRunTimeMinutes.Value,"Chart1_CategoryGroup1")
Replacing Chart1_CategoryGroup1 with what your Category Group is called.

How to merge adjactent polygons to 1 polygon and keep min/max data?

I have the following polygons in PostGIS
Each polygon has field with "Data" value.
I would like auto merge the polygons which touch each other :
1-2 and 3-4-5-6-7
Also , If possible I would like to have the Min/Max values from the columns of each polygon kept to the new polygon
Id Data Geom
1 8.45098 MULTIPOLYGON(((178253.411393551 665205.232423685,178248.411393552 665205.232423685,178248.411393552 665210.232423684,178253.411393551 665210.232423684,178253.411393551 665205.232423685)))
2 10.7918 MULTIPOLYGON(((178258.411393551 665205.232423685,178253.411393551 665205.232423685,178253.411393551 665210.232423684,178258.411393551 665210.232423684,178258.411393551 665205.232423685)))
3 10.7918 MULTIPOLYGON(((178263.411393552 665185.232423682,178258.411393551 665185.232423682,178258.411393551 665190.232423685,178263.411393552 665190.232423685,178263.411393552 665185.232423682)))
4 10.4139 MULTIPOLYGON(((178268.411393553 665185.232423682,178263.411393552 665185.232423682,178263.411393552 665190.232423685,178268.411393553 665190.232423685,178268.411393553 665185.232423682)))
5 7.448 MULTIPOLYGON(((178263.411393552 665180.232423684,178258.411393551 665180.232423684,178258.411393551 665185.232423682,178263.411393552 665185.232423682,178263.411393552 665180.232423684)))
6 10.2318 MULTIPOLYGON(((178268.411393553 665180.232423684,178263.411393552 665180.232423684,178263.411393552 665185.232423682,178268.411393553 665185.232423682,178268.411393553 665180.232423684)))
7 10.998 MULTIPOLYGON(((178263.411393552 665175.232423685,178253.411393551 665175.232423685,178253.411393551 665180.232423684,178258.411393551 665180.232423684,178263.411393552 665180.232423684,178263.411393552 665175.232423685)))
8 10.7548 MULTIPOLYGON(((178263.411393552 665175.232423685,178253.411393551 665175.232423685,178253.411393551 665180.232423684,178258.411393551 665180.232423684,178263.411393552 665180.232423684,178263.411393552 665175.232423685)))
What will be the easiest way to do it (I have little knowledge in QGIS/ArcMap and better knowledge with PostGIS ) ?
The only way I could figure out how to do this, was to create a table of unioned geometries in a CTE, use ST_Dump to produce individual polygons (ie, 1-2 and 3-4-5-6 in your question) and then select the max and min values of the data attributes from the original table (which I have called polygons, as you didn't specify a name), that intersect with the new unioned geometries, and grouping by the same new unioned geometries.
WITH geoms (geom) as
(SELECT (ST_Dump(ST_Union(geom))).geom from polygons)
SELECT max(data), min(data), g.geom
FROM polygons p, geoms g
WHERE St_Intersects(s.geom, g.geom)
GROUP BY g.geom;
If you want to save this to a new table, then add CREATE TABLE new_table AS in front of the WITH. There may be a more efficient way, but this works. In your question, your input polygons are MutliPolygons, so if you want this in the output also, add ST_Multi in front of the new unioned geometry. Putting that all together, you get something like:
CREATE TABLE Unioned_geometries AS
WITH geoms (geom) as
(SELECT (ST_Dump(ST_Union(geom))).geom from polygons)
SELECT max(data), min(data), ST_Multi(g.geom)
FROM polygons p, geoms g
WHERE St_Intersects(s.geom, g.geom)
GROUP BY g.geom;
You can use ST_Dump and ST_Union, but you will have problem on bigger data, if you will UNION milions of polygons, your geometry will be very very complex and PostGIS isn`t designed to work with big, complex geometries. You can use topology, or somethink like this
CREATE TABLE block_buildings AS
SELECT
block_id
, ST_MemUnion(geometry)
FROM houses building
, LATERAL (
with recursive building_block AS (
SELECT building.id
UNION
SELECT building2.id FROM building_block
JOIN houses build_geom USING(id)
JOIN houses building2
ON st_dwithin(build_geom.geometry, building2.geometry, 0.5)
)
SELECT md5(string_agg(id::text, ',' order by id)) block_id FROM building_block JOIN houses USING(id)
) block
GROUP BY block_id
;
LATERAL works like for loop, subquery is evaluated for every row. WITH recursive is common table expression, it works recursive, like snowball. ST_DWithin is used because of optimalization, you can use dump on outgoing geometries, if you want merge only polygons with shared boundary, or overlaps. It is slow, but not so much memory consuming (because of lateral), it can be optimalized (for example with plpgsql), because every group is computed for all its polygons. But you can use in aggregate query some aggregates for atrs. If you will create only geometry, you can agregate attrs into using ST_With and ST_PointOnSurface, it is pretty fast, if is well indexed.
-------- edit
In actual PostGIS are functions for clustering
this or this or this
This functions

Multi join issue

*EDIT** Thanks for all the input, and sorry for late reply. I have been away during the weekend without access to internet. I realized from the answers that I needed to provide more information, so people could understand the problem more throughly so here it comes:
I am migrating an old database design to a new design. The old one is a mess and very confusing ( I haven't been involved in the old design ). I've attached a picture of the relevent part of the old design below:
The table called Item will exist in the new design as well, and it got all columns that I need in the new design as well except one and it is here my problem begin. I need the column which I named 'neededProp' to be associated( with associated I mean like a column in the new Item table in the new design) with each new migrated row from Item.
So for a particular eid in table Environment there can be n entries in table Item. The "corresponding" set exists in table Room. The only way to know which rows that are associated in Item and Room are with the help of the columns "itemId" and "objectId" in the respective table. So for example for a particular eid there might be 100 entries in Item and Room and their "itemId" and "objectId" can be values from 1 to 100, so that column is only unique for a particular eid ( or baseSeq which it is called in table BaseFile).
Basically you can say that the tables Environment and BaseFile reminds of each other and the tables Item and Room reminds of each other. The difference is that some tables lack some columns and other may have some extra. I have no idea why it is designed like this from the beginning.
My question is if someone can help me with creating a query so that I can be able to find out the proper "neededProp" for each row in the Item-table so I can get that data into the new design?
*OLD-PART**This might be a trivial question but I can't get it to work as I want. I want to join a few tables as in the sql-statement below. If I start like this and run this query
select * from Environment e
join items ei on e.eid = ei.eid
I get like 400000 rows which is what I want. However if I add one more line so it looks like this:
select * from Environment e
join items ei on e.eid= ei.eid
left join Room r on e.roomnr = r.roomobjectnr
I get an insane amount of rows so there must be some multiplication going on. I want to get the same amount of rows ( like 400000 in this case ) even after joining the third table. Is that possible somehow? Maybe like creating a temporary view with the first 2 rows.
I am using MSSQL server.
So without knowing what data you have in your second query it's very difficult to say exactly how to write this out, and you're likely having a problem where there's an additional column that you are joining to in Rooms that perhaps you have forgotten such as something indicating a facility or hallway perhaps where you have multiple 'Room 1' entries as an example.
However, to answer your question regarding another way to write this out without using a temp table I've crufted up the below as an example of using a common table expression which will only return one record per source row.
;WITH cte_EnvironmentItems AS (
SELECT *
FROM Environment E
INNER JOIN Items I ON I.eid = E.eid
), cte_RankedRoom AS (
SELECT *
,ROW_NUMBER() OVER (ORDER BY R.UpdateDate DESC) [RN]
FROM Room R
)
SELECT *
FROM cte_EnvironmentItems E
LEFT JOIN cte_RankedRoom R ON E.roomnr = R.roomobjectnr
AND R.RN = 1
btw,do you want column from room table.if no then
select * from Environment e
join items ei on e.eid= ei.eid
where e.roomnr in (select r.roomobjectnr from Room r )
else
select * from Environment e
join items ei on e.eid= ei.eid
left join (select distinct roomobjectnr from Room) r on e.roomnr = r.roomobjectnr

SQL Server 2008 R2 STcontains spatial join using two tables

I've been stabbing at this for a while and am getting nowhere, so I'm hoping that someone with greater skills than I might have the answer.
I have two tables and in one is a set of latitude and longitude coordinates as separate columns. In the second able I have polygon shapes set in to a spatial geometry column.
The goal is to select all of the latitude and longitude pairs from table 1, which might be called separately as:
SQLSTRING = "SELECT LAT,LONG FROM dbo.Table1;"
The second table can be called using a scripting language loop to parse through each result one by one by using the following query:
SQLSTRING = "SELECT * FROM dbo.Table2 a WHERE a.POLY.STContains(geometry::STPointFromText('POINT(" & -Text Longitude Value from Table 1- & " " & -Text Latitude Value from Table 1- & ")',0))=1;"
So, my dilemma is that it surely would be possible to select all items from Table 1 and run them through a query that will only return those results where the latitude and longitude from table 1 are contained within any specified polygon stored in table 2. The scripting language loop is so obviously inefficient, so a single SQL query that could replace this and just return any matches would be a major time and resource saver.
Any help or pointers would be most gratefully appreciated. Thank you, in advance, for your advice.
Since you're working with spatial data, you can do a cross join (join all the rows from both tables together), then filter out what matches.
SELECT *
FROM dbo.Table2 AS a
, dbo.Table1 AS b
WHERE a.POLY.STContains(geometry::STPointFromText('POINT('+CAST(b.LONG AS VARCHAR)+' '+CAST(b.LAT AS VARCHAR)+')',0))=1;
One problem with performance here is that it will need to generate the geometry object repeatedly. It would be better if you could create a column to hold the geometry for table1. Make sure you have an spatial index on POLY in Table2 also.

Resources