PostGIS - Assign value based on ST_Intersects and ST_Area - sql

I have two data sets, US County boundaries (tiger.counties2020) and parcel data (temp.parcel). I would like to assign a county geoid to the parcel data based on a majority of the parcel polygon being within a county boundary. In some cases these parcel polygons overlap multiple counties, but I would like to assign the geoid to the county that the parcel has most of its area in.
UPDATE
temp.parcel
SET
geoid_majority = upd.geoid
FROM
tiger.counties2020 upd
WHERE
upd.geoid = '00000'
AND ST_Intersects(temp.parcel.geom_polygon, upd.geom) = TRUE
AND 51 <= 100 *(
ST_Area(
ST_Intersection(temp.parcel.geom_polygon, upd.geom)
) / LEAST(
ST_Area(temp.parcel.geom_polygon),
ST_Area(upd.geom)
)
)
AND geoid_majority IS NULL;
is what has yielded me the most results, however I still have parcels with a NULL value for geoid_majority

Related

Get Lowest level in tree created with hierarchyId that respect some conditions

I have created a hierarchy table in my SQL Server. I have a column hierarchyId. Each level of hierarchy represent a geographical/political level of a country:
Countries
Regions
Provinces
For each row I can fill some boundaries or not. To simplify I replace in the example the geometry column with a bit column.
I need to get lowest level that has boundaries filled. If at least one child has boundaries, surely also parent has boundaries.
I make an example:
For example, I have that tree. In my query I should get green and red areas. In this moment I get only green areas.. so, I should get:
Valle d'aosta because is the lowest level and it has boundaries (and that is OK);
Milano and Brescia because they are the lowest level with boundaries. I should not get Bergamo because it has no boundaries, but I should get Lombardia instead of Bergamo;
Italy because both Piemonte and Torino have no boundaries;
Lazio because Roma has no boundaries.
My query is partially correct.. I get all lowest levels. But I do not get the minimum high level that respect my condition..
I share a link with the example: http://sqlfiddle.com/#!18/878577/1
Here also the query you can see in sql fiddler:
select * from country_subdivisions cs
where IsoCode IN(
select cs.IsoCode
from parent p
where cs.Level.IsDescendantOf(p.Level) = 1
and p.CountryISOAlpha2Code = cs.CountryISOAlpha2Code
-- and (cs.Level.GetLevel() = p.Level.GetLevel() + 1 or cs.Level.GetLevel() = p.Level.GetLevel())
and cs.Level.GetLevel() = (SELECT MAX(leaf.Level.GetLevel()) FROM country_subdivisions leaf WHERE leaf.Level.IsDescendantOf(cs.Level) = 1 and leaf.HasBoundaries = 1)
)
As you can see I correctly get the green areas but not the red one.
Any Idea? Do I have been clear?
Thank you
I think the logic is summarized as follows:
Return a parent when:
That parent has boundaries, and
Either:
it has no children, or
it has has at least one child that has no boundaries.
This could be formulated as follows:
select parent.*
from country_subdivisions parent
where parent.HasBoundaries = 1
and 0 < (select case
when count(*) = 0 then 1
else count(case when child.HasBoundaries = 0 then 1 end)
end
from country_subdivisions child
where child.Level.GetAncestor(1) = parent.Level
);

Complex SQL query for a large record set

I have 3 tables. Lets start by explaining the first one
tblDistance: (airport1, airport2, distance) // airport1 and airport 2 are airport codes
This table contains the distances in miles between all airports of America there are a total of 3745 airports and the distances were calculated using a nested for loop and with each loop the counter was decremented. So for the 1st airport we calculated 3744 distances. For the second we calculated 3743 distances as we have already calculated its distance in the first loop with the first airport. Now lets say the first airport was Animas Air Park(K00C) and the second aiport is Broadus Airport(K00F). The records would appear in tblDistance as
(KOOC, other3744aiports, distance)
For second airport
(K00C, K00F, distance) //This one record has been already calculated in 1st iteration of the loop
(KOOF, other3743aiports, distance)
So except for the 1st airport if we want to find all the distances for a particular airport lets say K00F we need a union query given below.
(SELECT * FROM tblDistances WHERE tblDistances.airport1 = 'K00F')
UNION ALL
(SELECT * FROM tblDistances WHERE tblDistances.airport2 = 'K00F');
I hope I have explained it clearly. Now lets come to the other 2 tables. They are called tblHave and tblNeed
tblHave: (departure, departCode, arrival, arrivalCode, flightDate)
tblNeed: (departure, departCode, arrival, arrivalCode, flightDate)
Departure is the name of the airport from which the flight will depart and the departCode(K00C, K00F) is the code of the airport and same goes for arrival and arrivalCode.
Assume that we have a flight from (departure) San Francisco Intl (KSFO) to (arrival) South Bend Rgnl (KSBN) in the tblNeed. Now comes the real problem we have to find all the flights in the tblHave that are
On the same date as the given flight and
Departure airport is (KSFO) or within 500 miles of San Francisco Intl (KSFO) using union as explined above (lets call it qryDepart) AND
Arrival airport is (KSBN) or within 500 miles of South Bend Rgnl (KSBN) using union as explined above (lets call it qryArrival)
Sample qryArrival
SELECT tblDistances.airport2 as nearBy
FROM tblDistances
WHERE tblDistances.airport1 = 'KSFO' AND (((Abs([tblDistances].[distance]))<=500))
UNION ALL SELECT tblDistances.airport1 as nearBy
FROM tblDistances
WHERE tblDistances.airport2 = 'KSFO' AND (((Abs([tblDistances].[distance]))<=500));
I cannot figure out how can I find this and also the total no.of distance commbinations for all airports is more than 7 million. The records are in Access database. What I have figure is that I find nearby departure airports and nearby arrival airports from tblDistances and then use the IN clause to find the final results
Select * from tblHave where arrivalCode IN (qryArrival) AND departCode IN (qryDepart) AND Date = #dd/mm/yyyy#;
this is not working and the union takes too much time as the no of records is very large.
You don't need to use UNION here. You can do it in one query which should cut your execution time in half at least since you won't be checking every record twice. You can use a nested iif statement to determine which field to use for nearBy, and then change your WHERE to check both fields of the record. Like this:
SELECT
iif(
tblDistances.airport = 'KSFO',
tblDistances.airport2,
iif(tblDistances.airport2 = 'KSFO',
tblDistances.airport1,
null)
) as nearBy
FROM tblDistances
WHERE
(
tblDistances.airport1 = 'KSFO'
OR tblDistances.airport2 = 'KSFO'
)
AND (((Abs([tblDistances].[distance]))<=500))
Which would be much easier to read if you used a CASE statement, but Access doesn't support CASE. The above query does the same thing as:
SELECT
CASE
WHEN tblDistances.airport1 = 'KSFO' then tblDistances.airport2
WHEN tblDistances.airport2 = 'KSFO' then tblDistances.airport1
END as nearBy
FROM tblDistances
WHERE
(
tblDistances.airport1 = 'KSFO'
OR tblDistances.airport2 = 'KSFO'
)
AND (((Abs([tblDistances].[distance]))<=500))

Oracle Spatial Area Overlap Percentage

I am working with 2 tables containing polygons as shown hereunder.
I have many parkingzones and two special areas contained in the dangerareas table.
Some of the parking zones overlap with these areas, my purpose is two know the extent of this overlap as an area overlap percentage per parking.
I want a null results for the not overlaping parkings.
I am working with SQL Oracle Developper.
I can't figure out why this query is not working.
(I have adapted the layer names as I can't use the real ones, I hope there is no typo left).
My query returns an error :
ORA-13011: value is out of range ORA-06512: at "MDSYS.SDO_GEOM", line
125 ORA-06512: at "MDSYS.SDO_GEOM", line 1878
13011. 00000 - "value is out of range"
db = CITY
Table 1 : parkingzones PK
id_parking
SHAPE
Table 2 : dangerareas DA
id_area
SHAPE
SELECT
(area_overlaps/area_zones*100) as pct_overlap
FROM
(SELECT(SDO_GEOM.SDO_AREA
(SDO_GEOM.SDO_INTERSECTION(PK.SHAPE,DA.SHAPE,0,05)0,05)) area_overlaps
FROM CITY.parkingzones PK
CITY.dangerareas DA),
(SELECT (SDO_GEOM.SDO_AREA(PK.SHAPES,0.05)) area_zones )
FROM CITY.parkingzones PK);
Total parking area:
select sum(SDO_GEOM.SDO_AREA(PK.SHAPES,0.05)) total_parking_area
FROM CITY.parkingzones PK
Total overlapping area:
SELECT sum(SDO_GEOM.SDO_AREA(
SDO_GEOM.SDO_INTERSECTION(PK.SHAPE,DA.SHAPE,0.05),0.05))) total_overlap
FROM CITY.parkingzones PK
,CITY.dangerareas DA
where sdo_anyinteract(PK.shape,DA.shape) = 'TRUE';
Overlap per parking (only that have overlap)
select (sdo_geom.sdo_area(sdo_geom.sdo_intersection(pk.shape, da.shape, 0.05), 0.05) /
sdo_geom.sdo_area(pk.shape, 0.05)) * 100 percentage
from city.parkingzones pk
,city.dangerareas da
where sdo_anyinteract(pk.shape, da.shape) = 'TRUE';

Comparing SQL Queries

I'm considering two SQL queries (Oracle) and I shall state the difference between them by showing examples. The queries are as follows:
/* Query 1 */
SELECT DISTINCT countryCode
FROM Member M
WHERE NOT EXISTS(
(SELECT organisation FROM Member
WHERE countryCode = 'US')
MINUS
(SELECT organisation FROM Member
WHERE countryCode = M.countryCode ) )
/* Query 2 */
SELECT DISTINCT M1.countryCode
FROM Member M1, Member M2
WHERE M2.countryCode = 'US' AND M1.organisation = M2.organisation
GROUP BY M1.countryCode
HAVING COUNT(M1.organisation) = (
SELECT COUNT(M3.organisation)
FROM Member M3 WHERE M3.countryCode = 'US' )
As far as I get it, these queries give back the countries which are members of the same organisations as the United States. The scheme of Member is (countryCode, organisation, type) with bold ones as primary key. Example: ('US', 'UN', 'member'). The member table contains only a few tuples and is not complete, so when executing (1) and (2) both yield the same result (e.g. Germany, since here only 'UN' and 'G7' are in the table).
So how can I show that these queries can actually return different results?
That means how can I create an example table instance of Member such that the queries yield different results based on that table instance?
Thanks for your time and effort.
The queries will result all the country codes which are members at least with all the organization the US is member with (it could be member with other organizations as well).
I've finally found an example to show that they can actually output different values based on the same Member instance. This is actually the case when Member contains duplicates. For query 1 this is not a problem, but for query 2 it actually affects the result, since here the number of memberships is crucial. So, if you have e.g. ('FR', 'UN', member) twice in Member the HAVING COUNT(M1.organisation) will return a different value as SELECT COUNT(M3.organisation) and 'FR' would not be part of the output.
Thanks to all for your constructive suggestions, that helped me a lot.
The first query would return countries whose list of memberships is longer than that of the US. It does require they include the same organizations as US but it could be more.
The second one requires the two membership lists to be identical.
As for creating an example with real data, start with an empty table and add this row:
insert into Member (countryCode, organisation)
values ('Elbonia', 'League of Fictitious Nations')
By the way a full outer join would let you characterize the difference symmetrically:
select
mo.countryCode || ' ' ||
case
when count(case when mu.organisation is null then 1 else null end) > 0
and count(case when mo.organisation is null then 1 else null end) > 0
then 'and US both have individual memberships they that do not have in common.'
when count(case when mo.organisation is null then 1 else null end) > 0
then 'is a member of some organisations that US is not a member of.'
when count(case when mo.organisation is null then 1 else null end) > 0
then 'is not a member of some organisations that US is a member of.'
else 'has identical membership as US.'
end
from
(select * from Member where countryCode = 'US') mu
full outer join
(select * from Member where countryCode = '??') mo
on mo.organisation = mu.organisation
Please forgive the dangling prepositions.
And a disk note, though duplicate rows are not allowed in normalized data, this query has no problem with those.

Oracle Spatial Geometry covered by the most

I have a table which contains a number of geometries. I am attempting to extract the one which is most covered by another geometry.
This is best explained with pictures and code.
Currently I am doing this simple spatial query to get any rows that spatially interact with a passed in WKT Geometry
SELECT ID, NAME FROM MY_TABLE WHERE
sdo_anyinteract(geom,
sdo_geometry('POLYGON((400969 95600,402385 95957,402446 95579,400905 95353,400969 95600))',27700)) = 'TRUE';
Works great, returns a bunch of rows that interact in any way with my passed in geometry.
What I preferably want though is to find which one is covered most by my passed in geometry. Consider this image.
The coloured blocks represent 'MY_TABLE'. The black polygon over the top represents my passed in geometry I am searching with. The result I want returned from this is Polygon 2, as this is the one that is most covered by my polygon. Is this possible? Is there something I can use to pull the cover percentage in and order by that or a way of doing it that simply returns just that one result?
--EDIT--
Just to supplement the accepted answer (which you should go down and give an upvote as it is the entire basis for this) this is what I ended up with.
SELECT name, MI_PRINX,
SDO_GEOM.SDO_AREA(
SDO_GEOM.SDO_INTERSECTION(
GEOM,
sdo_geometry('POLYGON((400969.48717156524 95600.59583240788,402385.9445972018 95957.22742049221,402446.64806962677 95579.91508788493,400905.95874489535 95353.03765349534,400969.48717156524 95600.59583240788))',27700)
,0.005
)
,0.005) AS intersect_area
FROM LIFE_HEATHLAND WHERE sdo_anyinteract(geom, sdo_geometry('POLYGON((400969.48717156524 95600.59583240788,402385.9445972018 95957.22742049221,402446.64806962677 95579.91508788493,400905.95874489535 95353.03765349534,400969.48717156524 95600.59583240788))',27700)) = 'TRUE'
ORDER BY INTERSECT_AREA DESC;
This returns me all the results that intersect my query polygon with a new column called INTERSECT_AREA, which provides the area. I can then sort this and pick up the highest number.
Just compute the intersection between each of the returned geometries and your query window (using SDO_GEOM.SDO_INTERSECTION()), compute the area of each such intersection (using SDO_GEOM.SDO_AREA()) and return the row with the largest area (order the results in descending order of the computed area and only retain the first row).
For example, the following computes how much space Yellowstone National Park occupies in each state it covers. The results are ordered by area (descending).
SELECT s.state,
sdo_geom.sdo_area (
sdo_geom.sdo_intersection (
s.geom, p.geom, 0.5),
0.5, 'unit=sq_km') area
FROM us_states s, us_parks p
WHERE SDO_ANYINTERACT (s.geom, p.geom) = 'TRUE'
AND p.name = 'Yellowstone NP'
ORDER by area desc;
Which returns:
STATE AREA
------------------------------ ----------
Wyoming 8100.64988
Montana 640.277886
Idaho 154.657145
3 rows selected.
To only retain the row with the largest intersection do:
SELECT * FROM (
SELECT s.state,
sdo_geom.sdo_area (
sdo_geom.sdo_intersection (
s.geom, p.geom, 0.5),
0.5, 'unit=sq_km') area
FROM us_states s, us_parks p
WHERE SDO_ANYINTERACT (s.geom, p.geom) = 'TRUE'
AND p.name = 'Yellowstone NP'
ORDER by area desc
)
WHERE rownum = 1;
giving:
STATE AREA
------------------------------ ----------
Wyoming 8100.64988
1 row selected.
The following variant also returns the percentage of the park's surface in each intersecting state:
WITH p AS (
SELECT s.state,
sdo_geom.sdo_area (
sdo_geom.sdo_intersection (
s.geom, p.geom, 0.5),
0.5, 'unit=sq_km') area
FROM us_states s, us_parks p
WHERE SDO_ANYINTERACT (s.geom, p.geom) = 'TRUE'
AND p.name = 'Yellowstone NP'
)
SELECT state, area,
RATIO_TO_REPORT(area) OVER () * 100 AS pct
FROM p
ORDER BY pct DESC;
If you want to return the geometry of the intersections, just include that into your result set.