Point on a polygon boundary using postgis & geodjango - postgis

I have point data and multipolygon data in tables. How can I find points lying on multipolygon boundary using postgis and in geodjango?

I can't help you with geodjango, but I can give you a PostGIS query.
SELECT ST_Contains(ST_Boundary(ST_GeomFromText('POLYGON((1 1,0 0, -1 1, 1 1))')),
ST_GeomFromText(points.g))
FROM UNNEST(ARRAY['POINT(1 1)', 'POINT(0 1)']) points (g)
The key is to use ST_Boundary to get the polygon's boundary and check if that contains the point.

You can just use PostGIS SQL I guess:
SELECT points,area from points_table,area WHERE
area_geometry && points
AND ST_Contains(area_geometry,points)

Use ST_Covers it contains own boundary. But be careful in that case one point can be included by many (multi)polygons.
SELECT ST_Covers(ST_GeomFromText('POLYGON((1 1,0 0, -1 1, 1 1))'),
ST_GeomFromText(points.g))
FROM UNNEST(ARRAY['POINT(1 1)', 'POINT(0 1)', 'POINT(0 0.5)']) points (g)

Related

How to convert mdq.Similarity score to the number of edit operations in MDS, in MS SQL Server?

How can I convert the result of mdq.Similarity to the number of edits needed for the two words to match. This function is part of Master Data Service (MDS) in Microsoft SQL Server defined as:
USE [mds]
ALTER FUNCTION [mdq].[Similarity](#input1 [nvarchar](4000), #input2 [nvarchar](4000), #method [tinyint], #containmentBias [float], #minScoreHint [float])
RETURNS [float] WITH EXECUTE AS CALLER, RETURNS NULL ON NULL INPUT
AS EXTERNAL NAME [Microsoft.MasterDataServices.DataQuality].[Microsoft.MasterDataServices.DataQuality.SqlClr].[Similarity]
The two words that are 1 edit away from each other produce different Levenshtein distance, which seems to account for their length (number of characters in the word).
SELECT a=mds.mdq.Similarity('a','',0,0,0),
ab=mds.mdq.Similarity('ab','a',0,0,0),
abc=mds.mdq.Similarity('abc','ab',0,0,0),
ac=mds.mdq.Similarity('ac','ab',0,0,0)
a ab abc ac
0 0.5 0.67 0.5
Whereas I need it to return 1 in each case because each pair has two words differing by a single edit (insertion, deletion, substitution).
You can't.
I haven't been able to do anything that useful with this function. The main reason I'm posting this is so that people can access the msdn documentation as it does not come up when doing a google search.
Per msdn, mdq.Similarity:
Returns a similarity score. This score indicates the similarity
between two strings that are compared by a specified match algorithm.
I understand Levenshtein, Jaro, LCSS but, after playing around with this function for many hours, it's hard to do anything useful with it. mdq.Similarity is a dead end IMO.
This
--Using Levenshtein edit distance algorithm.
SELECT Lev = mdq.Similarity(N'Alexia Geogio', N'Alexandra George', 0, 0.0, 0.0);
--Using Jaccard similarity coefficient algorithm.
SELECT Jacc = mdq.Similarity(N'Alexia Geogio', N'Alexandra George', 1, 0.0, 0.0);
--Using Jaro-Winkler distance algorithm.
SELECT jaro = mdq.Similarity(N'Alexia Geogio', N'Alexandra George', 2, 0.0, 0.0);
--Using longest common subsequence algorithm.
SELECT lcss = mdq.Similarity(N'12345', N'93459', 3, 0.0, 0.0);
Returns
Lev
----------------------
0.5625
Jacc
----------------------
0.269230769230769
jaro
----------------------
0.878846153846154
lcss
----------------------
0.6
Inspired by Alan's answer, I digged around for normalized Levenshtein distance. Found this answer and, apparently, it works swell: denormalized mdq.Similarity outputs number of edit operations. So, MDS function computes
1- distance(a,b)/max(a.length, b.length)
So, we reverse engineer the original Levenshtein distance as (note the rounding!!):
CREATE FUNCTION fnLevDist(#a VARCHAR(100), #b VARCHAR(100), #minScoreHint FLOAT=0)
RETURNS INT AS
BEGIN
DECLARE #scaler REAL = CASE WHEN LEN(#a)>LEN(#b) THEN LEN(#a) ELSE LEN(#b) END
RETURN ROUND((1.0 - mds.mdq.Similarity(#a, #b, 0, 0, #minScoreHint)) * #scaler, 0)
END
GO
WITH txt AS(
SELECT a='a', b=''
UNION ALL SELECT a='ab', b='a'
UNION ALL SELECT a='abc', b='ab'
UNION ALL SELECT a='ac', b='ab' )
SELECT *, Lev=dbo.fnLevDist(a,b,0), Lev_nmzd=mds.mdq.Similarity(a,b, 0, 0, 0) FROM txt
I've retained the #minScoreHint parameter in the wrapping function because it can greatly improve performance (see doc). The output is then:
a b Lev Lev_nmzd
a 1 0
ab a 1 0.5
abc ab 1 0.667
ac ab 1 0.5
So, it's not so bad afterall. Still, I wish that Microsoft has cited the publication they build their machine learning work on. It is already a standard practice for package documentation in Python, R, and other software (GPL or otherwise).

Raster ST_Clip fails when a geometry barely intersects

I am trying to perform spatial statistics using postgis. Once in a while I have ST_Clip crushes and halt the query. I figure that this occurs when polygon barely intersects with raster. Please see the sample below.
SELECT ST_Summary(
ST_Clip(
ST_AddBand(
ST_MakeEmptyRaster(16, 16, 0, 0, 1, 1, 0, 0),
ARRAY[
ROW(1, '8BUI'::text, 0, 255),
ROW(2, '8BUI'::text, 0, 255),
ROW(3, '8BUI'::text, 0, 255)
]::addbandarg[]
)
-- this works
--, ST_GeomFromText('POLYGON((15.999999 15.999999, 15.999999 17, 17 17, 17 15.999999, 15.999999 15.999999))')
-- this fails
, ST_GeomFromText('POLYGON((15.9999999 15.9999999, 15.9999999 17, 17 17, 17 15.9999999, 15.9999999 15.9999999))')
)
);
With the above query I am getting following error.
psql:demo_clip_fail_barelyintersects.sql:16: ERROR: RASTER_clip: Could not get band from working raster
CONTEXT: PL/pgSQL function st_clip(raster,integer[],geometry,double precision[],boolean) line 8 at RETURN
I am hoping to getting no record returned instead, or some kind of empty raster. In my production code, the geometry/raster pair was found by ST_Intersects(r.rast, p.geom) between table of polygons and raster. One way I thought about making bounding box for raster which is slightly smaller than the extent of raster, but this is pretty ugly...
My version of postgres and postgis are
PostgreSQL 9.6.1 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.9.1,
64-bit
POSTGIS="2.3.1 r15264" GEOS="3.6.0-CAPI-1.10.0 r0" PROJ="Rel.
4.9.3, 15 August 2016" GDAL="GDAL 2.1.2, released 20 16/10/24" LIBXML="2.9.4" LIBJSON="0.12.1" RASTER
Thanks!
My tentative solution, wrap with begin/exception/end block and let the exception part to return empty raster. Performance suffers (~ two times). It will create false negative, but not sure what to look for...
-- function to work around bug in st_clip (fails when polygon barely intersects with raster)
-- not sure how much damage this has on performance
create or replace function st_clip_fuzzy(
rast raster, nband integer[],
geom geometry,
nodataval double precision[] DEFAULT NULL, crop boolean DEFAULT TRUE
)
returns raster
as $$
declare
rec record;
g geometry;
begin
return st_clip($1, $2, $3, $4, $5);
exception
when others then
select st_intersection(st_envelope(rast), geom) into g;
raise warning 'st_clip_fuzzy: intersection %', st_astext(g);
raise warning 'st_clip_fuzzy: area intersection %', st_area(g);
raise warning 'st_clip_fuzzy: area pixel %', abs(ST_ScaleX(rast) * ST_ScaleY(rast));
raise warning 'st_clip_fuzzy: area ratio %', st_area(g) / abs(ST_ScaleX(rast) * ST_ScaleY(rast));
return ST_MakeEmptyRaster(0, 0, ST_UpperLeftX(rast), ST_UpperLeftY(rast), ST_ScaleX(rast), ST_ScaleY(rast), ST_SkewX(rast), ST_SkewY(rast), ST_SRID(rast));
end;
$$ language 'plpgsql' immutable;
CREATE OR REPLACE FUNCTION st_clip_fuzzy(
rast raster, nband integer,
geom geometry,
nodataval double precision, crop boolean DEFAULT TRUE
)
-- four more interfaces with different set of arguments

Find the points using Oracle spatial directly in front of a polgon

I have many polygons and many points and want to find the point such that when a line is drawn between that point and the polygon it does not intersect with any other polygon. So basically I need a point that is very close to the polygon and no other polygon is between them.
I tried the following query and it gives me all the points whether or not they are being intersected by a polygon or not.
SELECT P.POINTLOC from pointTable P WHERE NOT MDSYS.SDO_OVERLAPBDYINTERSECT(P.POINTLOC," +
"MDSYS.SDO_GEOMETRY(2003,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1)," +
"MDSYS.SDO_ORDINATE_ARRAY(4, 226, 150, 254, 164, 240, 191, 212, 176,4,226))) = 'TRUE'";
Then I tried this query and it gives some correct points but miss a few correct points:
SELECT P.POINTLOC from pointTable P WHERE MDSYS.SDO_WITHIN_DISTANCE(P.POINTLOC," +
"MDSYS.SDO_GEOMETRY(2003,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1)," +
"MDSYS.SDO_ORDINATE_ARRAY(4, 226, 150, 254, 164, 240, 191, 212, 176,4,226)),'distance = 40') = 'TRUE'";
Can some one point out which Oracle spatial operator would be best suited for this situation?
It sounds like you're looking for a Nearest Neighbour implementation - Oracle provides the Spatial operator SDO_NN.
You can find more details here:
http://docs.oracle.com/database/121/SPATL/sdo_operat.htm#SPATL1032
This will only get you so far, in that it'll find the nearest point to a polygon, but it won't guarantee that there are no polygons between your point and your target polygon. I suspect if you want to ensure this, you'll have have to get creative.
My approach would be:
use SDO_NN to get the closest point or points
use SDO_GEOM.SDO_CENTROID to find the polygon centre of gravity
create an in-query/in-memory SDO_GEOMETRY line that joins the two points
use this as the basis of a NOT EXISTS clause to exclude points where a polygon intersects that line
Something like the following untested / not-quite finished example, perhaps:
SELECT *
FROM points pnt
WHERE sdo_nn(pnt.point, sdo_geometry(your polygon here)) = 'TRUE' -- fill this with your polygon
AND NOT EXISTS (
SELECT 1
FROM polygons plg
WHERE sdo_geom.sdo_intersection(
plg.polygon
, sdo_geometry(
2002
, NULL -- oracle srid
, NULL -- point
, sdo_elem_info_array(1, 2, 1) -- line
, sdo_ordinate_array(
, sdo_geom.sdo_centroid(
sdo_geometry(your polygon here) -- fill this with your polygon
, 0.05
).x
, sdo_geom.sdo_centroid(
sdo_geometry(your polygon here) -- fill this with your polygon
, 0.05 -- tolerance
).t
, pnt.point.sdo_point.x
, pnt.point.sdo_point.y
) -- line between point and polygon centroid
)
, 0.05 -- tolerance
) = 'TRUE'
)
Depending on your dataset/performance, you might want to do some of this in PL/SQL using collections or loops.
The example above is a bit rough and ready, but I hope you get the gist.

How to expand a polygon to reach a nearby line

I would like to expand a polygon so that it fills an empty space between itself and a nearby (and touching in two points) line, as in the image posted here. As you can see the blue linestring makes an empty space on top of the pink polygon and I want to fill it with the polygon. Is there a postgis solution to this ? I havent' found any "easy" way.
Thanks !
The solution is similar to the one I presented here. Only in this case you need to buff up the linestring a bit.
WITH p AS (
SELECT ST_MakePolygon(ST_GeomFromText('LINESTRING(0 0,1 0,1 1, 0 1, 0 0)')) as geo
),
l AS (
SELECT ST_BUFFER(ST_GeomFromText('LINESTRING(0.0 0.0,0.5 0, 0.7 -1, 1 0)'),0.000000000000001) as geo
),
bigpoly AS(
SELECT ST_UNION(geo) as geom
FROM(
SELECT geo FROM p
UNION ALL
SELECT geo FROM l) as q
)
SELECT ST_BUFFER(ST_BuildArea(ST_InteriorRingN(geom,i)),0.000000000000001) as geo
FROM bigpoly
CROSS JOIN generate_series(1,(SELECT ST_NumInteriorRings(geom) FROM bigpoly)) as i
This will give you the missing piece, now you just need to ST_UNION it with the rest, you might also want to check if it's really a correct one if your original polygon contains holes.

STintersects() to find the Intersection point

I have two sql server geometry-ies which I am using to check if they intersect. If they do I need the intersection point.
Currently I can get only Boolean output where if it intersects = 1 and if it does not intersect it will give =0 . Is there any way I can find the intersection of two shapes in geometry?
Update this question led to my next question concerning how can one check if a point (lat/long) exists in a region which has 4000 points (lat/long). Can one use stcontains or stintersects on geography?
sql - STContains on Geography column
DECLARE #line1 GEOMETRY = geometry::STGeomFromText('LINESTRING(0 0, 1 1)', 0)
DECLARE #line2 GEOMETRY = geometry::STGeomFromText('LINESTRING(1 0, 0 1)', 0)
SELECT #line1.STIntersection(#line2).ToString()

Resources