Gridding a geometry object (PostGIS)
Tuesday December 20 2011 at 22:51
Keywords grid gridding raster vector chopping intersection boundary clip polygon line
Summary A vector-to-raster algorithm generating tiles covering (multi-)line/polygon geometries. Boundary tiles are clipped forming non-square tiles.
A common question that comes up in the various database forums (PostGIS, SQL Server, Oracle) is how to “grid” a linear or polygonal object.
By “grid” one means work out the square pixels (rectangular polygons) that cover or define a vector geometry.
Here is some SQL for doing this for PostGIS.
Single Geometry
The following grids a single geometry into a single logical grid. The function Morton and REGULARGRIDXY are provided in other blog articles or at the end of this article. Note that REGULARGRIDXY is used in order to ensure that the gridding takes place according to a commonly defined MBR .
WITH geomQuery AS (
SELECT ST_XMIN( g. geom) ::NUMERIC AS xmin, round( ST_XMAX( g. geom) ::NUMERIC , 2 ) ::NUMERIC AS xmax,
ST_YMIN( g. geom) ::NUMERIC AS ymin, round( ST_YMAX( g. geom) ::NUMERIC , 2 ) ::NUMERIC AS ymax,
g. geom, 0.050 ::NUMERIC AS gridX, 0.050 ::NUMERIC AS gridY, 0 ::int4 AS loCol, 0 ::int4 AS loRow
FROM ( SELECT ST_SymDifference( ST_Buffer( a. geom, 1.000 ::NUMERIC ) , ST_Buffer( a. geom, 0.50 ::NUMERIC ) ) AS geom
FROM ( SELECT ST_GeomFromText( 'MULTIPOINT((09.25 10.00),(10.75 10.00),(10.00 10.75),(10.00 9.25))' , 0 ) AS geom ) AS a
) AS g
)
SELECT ROW_NUMBER ( ) OVER ( ORDER BY f. gcol, f. grow) AS tid,
morton( ( f. gcol- f. loCol) , ( f. grow- f. loRow) ) AS mkey,
f. gcol,
f. grow,
COUNT ( * ) AS UnionedTileCount,
ST_Union( f. geom) AS geom
FROM ( SELECT CASE WHEN ST_GeometryType( b. geom) IN ( 'ST_Polygon' , 'ST_MultiPolygon' )
THEN ST_Intersection( b. ageom, b. geom)
ELSE b. geom
END AS geom,
b. gcol, b. grow, b. loCol, b. loRow
FROM ( SELECT a. geom AS ageom, a. loCol, a. loRow,
( RegularGridXYSQL( a. xmin, a. ymin, a. xmax, a. ymax, a. gridX, a. gridY, ST_Srid( a. geom) ) ) .*
FROM geomQuery AS a
) AS b
WHERE ST_Intersects( b. ageom, b. geom)
) AS f
WHERE POSITION ( 'POLY' IN UPPER ( ST_AsText( f. geom) ) ) > 0
GROUP BY f. gcol, f. grow, f. loCol, f. loRow
ORDER BY 2 ;
This is what this looks like.
Multiple Geometries
The following SQL grids multiple geometries into a single logical grid.
This aggregate ST_XMin etc SQL and RegularGridXY function ensures that the grids for one object align with the grids for the others.
WITH geomQuery AS (
SELECT g. rid,
( MIN ( ST_XMIN( g. geom) ) OVER ( partition BY g. pid) ) ::NUMERIC AS xmin,
( MAX ( round( ST_XMAX( g. geom) ::NUMERIC , 2 ) ) OVER ( partition BY g. pid) ) ::NUMERIC AS xmax,
( MIN ( ST_YMIN( g. geom) ) OVER ( partition BY g. pid) ) ::NUMERIC AS ymin,
( MAX ( round( ST_YMAX( g. geom) ::NUMERIC , 2 ) ) OVER ( partition BY g. pid) ) ::NUMERIC AS ymax,
g. geom, 0.050 ::NUMERIC AS gridX, 0.050 ::NUMERIC AS gridY, 0 ::int4 AS loCol, 0 ::int4 AS loRow
FROM ( SELECT 1 ::int4 AS pid, a. rid, ST_SymDifference( ST_Buffer( a. geom, 1.000 ::NUMERIC ) , ST_Buffer( a. geom, 0.750 ::NUMERIC ) ) AS geom
FROM ( SELECT 1 ::int4 AS rid, ST_GeomFromText( 'POINT(09.50 10.00)' , 0 ) AS geom
UNION ALL SELECT 2 ::int4 AS rid, ST_GeomFromText( 'POINT(10.50 10.00)' , 0 ) AS geom
UNION ALL SELECT 3 ::int4 AS rid, ST_GeomFromText( 'POINT(10.00 10.50)' , 0 ) AS geom
UNION ALL SELECT 4 ::int4 AS rid, ST_GeomFromText( 'POINT(10.00 09.50)' , 0 ) AS geom ) a
) g
)
SELECT ROW_NUMBER ( ) OVER ( ORDER BY f. gcol, f. grow) AS tid,
morton( ( f. gcol- f. loCol) , ( f. grow- f. loRow) ) AS mkey,
f. gcol,
f. grow,
COUNT ( * ) AS UnionedTileCount,
ST_Union( f. geom) AS geom
FROM ( SELECT CASE WHEN ST_GeometryType( b. geom) IN ( 'ST_Polygon' , 'ST_MultiPolygon' )
THEN ST_Intersection( b. ageom, b. geom)
ELSE b. geom
END AS geom,
b. gcol, b. grow, b. loCol, b. loRow
FROM ( SELECT a. geom AS ageom, a. loCol, a. loRow,
( RegularGridXYSQL( a. xmin, a. ymin, a. xmax, a. ymax, a. gridX, a. gridY, ST_Srid( a. geom) ) ) .*
FROM geomQuery a
) b
WHERE ST_Intersects( b. ageom, b. geom)
) f
WHERE POSITION ( 'POLY' IN UPPER ( ST_AsText( f. geom) ) ) > 0
GROUP BY f. gcol, f. grow, f. loCol, f. loRow
ORDER BY 2 ;
This is what this looks like.
The RegularGridXY and RegularGridSQL functions are:
CREATE OR REPLACE FUNCTION RegularGridXY( p_xmin NUMERIC ,
p_ymin NUMERIC ,
p_xmax NUMERIC ,
p_ymax NUMERIC ,
p_TileSizeX NUMERIC ,
p_TileSizeY NUMERIC ,
p_srid int4)
RETURNS SETOF T_Grid IMMUTABLE
AS $$
DECLARE
v_loCol int4;
v_hiCol int4;
v_loRow int4;
v_hiRow int4;
v_geom geometry;
v_grid t_grid;
BEGIN
v_loCol := trunc( ( p_XMIN / p_TileSizeX) ::NUMERIC ) ;
v_hiCol := CEIL ( ( p_XMAX / p_TileSizeX) ::NUMERIC ) - 1 ;
v_loRow := trunc( ( p_YMIN / p_TileSizeY) ::NUMERIC ) ;
v_hiRow := CEIL ( ( p_YMAX / p_TileSizeY) ::NUMERIC ) - 1 ;
FOR v_col IN v_loCol.. v_hiCol Loop
FOR v_row IN v_loRow.. v_hiRow Loop
v_geom := ST_SetSRID( ST_MakeBox2D( ST_Point( ( v_col * p_TileSizeX) , ( v_row * p_TileSizeY) ) ,
ST_Point( ( ( v_col * p_TileSizeX) + p_TileSizeX) , ( ( v_row * p_TileSizeY) + p_TileSizeY) ) ) ,
p_srid) ;
SELECT v_col, v_row, v_geom INTO v_grid;
-- SELECT v_col,v_row,ST_GeomFromText('POINT(' || v_col || ' ' || v_row ||')',0) INTO v_grid;
RETURN NEXT v_grid;
END Loop;
END Loop;
END ;
$$ LANGUAGE 'plpgsql' ;
.
CREATE OR REPLACE FUNCTION RegularGridXYSQL( p_xmin NUMERIC ,
p_ymin NUMERIC ,
p_xmax NUMERIC ,
p_ymax NUMERIC ,
p_TileSizeX NUMERIC ,
p_TileSizeY NUMERIC ,
p_srid int4)
RETURNS SETOF T_Grid IMMUTABLE
AS
$$
SELECT * FROM RegularGridXY( $1, $2, $3, $4, $5, $6, $7) ;
$$
LANGUAGE 'sql' ;
I hope this is useful to a PostGIS user.
Article Navigation:
Previous
Comment